146 lines
6.2 KiB
C#
146 lines
6.2 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
namespace _Project.Scripts.LevelGenerating
|
||
{
|
||
// Куда смотрит видимая сторона стены (внутрь комнаты или коридора)
|
||
public enum WallFacing { XP, XN, ZP, ZN }
|
||
|
||
// Прямой отрезок стены по линии сетки (координаты в модулях).
|
||
// Start - точка на линии сетки, с которой отрезок начинается.
|
||
// Для ZP/ZN отрезок идёт вдоль +X, для XP/XN - вдоль +Z.
|
||
// RoomIndex - индекс комнаты в FloorData.Rooms, которой принадлежит стена.
|
||
// Поля публичные, чтобы потом сохранять в файл.
|
||
[System.Serializable]
|
||
public struct WallRun
|
||
{
|
||
public Vector2Int Start;
|
||
public int Length;
|
||
public WallFacing Facing;
|
||
public int RoomIndex;
|
||
|
||
public WallRun(Vector2Int start, int length, WallFacing facing, int roomIndex)
|
||
{
|
||
Start = start;
|
||
Length = length;
|
||
Facing = facing;
|
||
RoomIndex = roomIndex;
|
||
}
|
||
}
|
||
|
||
// =====================================================================
|
||
// Строит отрезки стен из FloorData. Спавна здесь нет, только данные.
|
||
//
|
||
// Правила:
|
||
// - Стена стоит на границе прямоугольника комнаты (плоскость без толщины).
|
||
// - У коридора стены только по двум длинным сторонам, торцы открыты.
|
||
// - В стенах комнат вырезаются проёмы шириной ровно с коридор.
|
||
// - Каждый отрезок помнит комнату-владельца (RoomIndex).
|
||
// =====================================================================
|
||
public static class ShellBuilder
|
||
{
|
||
private struct Gap
|
||
{
|
||
public bool Vertical; // линия x = Line (вертикальная), иначе z = Line
|
||
public int Line, From, To;
|
||
}
|
||
|
||
public static void Build(FloorData floor)
|
||
{
|
||
floor.Walls.Clear();
|
||
var gaps = CollectGaps(floor);
|
||
|
||
for (int i = 0; i < floor.Rooms.Count; i++)
|
||
{
|
||
var r = floor.Rooms[i];
|
||
int x0 = r.Position.x, x1 = x0 + r.Length;
|
||
int z0 = r.Position.y, z1 = z0 + r.Width;
|
||
|
||
if (r.rt == RoomType.Corridor)
|
||
{
|
||
// у коридора проёмов нет, его торцы просто открыты
|
||
if (IsAlongX(r))
|
||
{
|
||
AddSide(floor, i, false, z0, x0, x1, WallFacing.ZP, null);
|
||
AddSide(floor, i, false, z1, x0, x1, WallFacing.ZN, null);
|
||
}
|
||
else
|
||
{
|
||
AddSide(floor, i, true, x0, z0, z1, WallFacing.XP, null);
|
||
AddSide(floor, i, true, x1, z0, z1, WallFacing.XN, null);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AddSide(floor, i, false, z0, x0, x1, WallFacing.ZP, gaps); // южная
|
||
AddSide(floor, i, false, z1, x0, x1, WallFacing.ZN, gaps); // северная
|
||
AddSide(floor, i, true, x0, z0, z1, WallFacing.XP, gaps); // западная
|
||
AddSide(floor, i, true, x1, z0, z1, WallFacing.XN, gaps); // восточная
|
||
}
|
||
}
|
||
}
|
||
|
||
// Коридор идёт вдоль 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 List<Gap> CollectGaps(FloorData floor)
|
||
{
|
||
var gaps = new List<Gap>();
|
||
|
||
foreach (var c in floor.Corridors)
|
||
{
|
||
int x0 = c.Position.x, x1 = x0 + c.Length;
|
||
int z0 = c.Position.y, z1 = z0 + c.Width;
|
||
|
||
if (IsAlongX(c))
|
||
{
|
||
gaps.Add(new Gap { Vertical = true, Line = x0, From = z0, To = z1 });
|
||
gaps.Add(new Gap { Vertical = true, Line = x1, From = z0, To = z1 });
|
||
}
|
||
else
|
||
{
|
||
gaps.Add(new Gap { Vertical = false, Line = z0, From = x0, To = x1 });
|
||
gaps.Add(new Gap { Vertical = false, Line = z1, From = x0, To = x1 });
|
||
}
|
||
}
|
||
return gaps;
|
||
}
|
||
|
||
// Добавляет сторону [from, to] на линии line, вырезая проёмы
|
||
private static void AddSide(FloorData floor, int owner, bool vertical, int line, int from, int to,
|
||
WallFacing facing, List<Gap> gaps)
|
||
{
|
||
int cursor = from;
|
||
|
||
if (gaps != null)
|
||
{
|
||
var own = new List<Gap>();
|
||
foreach (var g in gaps)
|
||
if (g.Vertical == vertical && g.Line == line && g.To > from && g.From < to)
|
||
own.Add(g);
|
||
own.Sort((a, b) => a.From.CompareTo(b.From));
|
||
|
||
foreach (var g in own)
|
||
{
|
||
if (g.From > cursor) AddRun(floor, owner, vertical, line, cursor, g.From, facing);
|
||
cursor = Mathf.Max(cursor, g.To);
|
||
}
|
||
}
|
||
|
||
if (cursor < to) AddRun(floor, owner, vertical, line, cursor, to, facing);
|
||
}
|
||
|
||
private static void AddRun(FloorData floor, int owner, bool vertical, int line, int a, int b,
|
||
WallFacing facing)
|
||
{
|
||
Vector2Int start = vertical ? new Vector2Int(line, a) : new Vector2Int(a, line);
|
||
floor.Walls.Add(new WallRun(start, b - a, facing, owner));
|
||
}
|
||
}
|
||
} |