вроде порешал косяк лифта с поворотами. Мб не работает

This commit is contained in:
vladislove 2026-10-06 21:38:58 +03:00
parent 490c534cfa
commit 9519be076f
2 changed files with 145 additions and 27 deletions

View File

@ -13842,15 +13842,15 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::_Project.Scripts.LevelGenerating.WallsGenerator
floorsCount: 10
lengthWallMin: 10
lengthWallMax: 25
lengthWallMin: 20
lengthWallMax: 40
lengthStartRoom: 10
lengthLiftRoom: 10
widthLiftRoom: 5
lengthFloor: 100
corridorLength: 5
corridorWigth: 4
_minDepthLiftRoom: 0
_minDepthLiftRoom: 7
GameObjectA: {fileID: 8066171868259172852, guid: e46748b8eb8a6074d9bb87701cebe84b, type: 3}
GameObjectB: {fileID: 4641718306729093824, guid: 3905eab8a5f6b3049a83378b1acf1ebc, type: 3}
maxRooms: 15

View File

@ -26,6 +26,10 @@ namespace _Project.Scripts.LevelGenerating
private int _curRooms;
private readonly Dictionary<Room, int> _depth = new Dictionary<Room, int>();
private readonly Dictionary<Room, (Room parent, ExitsInRoomType dir)> _parent
= new Dictionary<Room, (Room, ExitsInRoomType)>();
[SerializeField] private bool liftDoorAnyPosition = false; // true, если дверь в префабе может быть не по центру
private readonly Dictionary<Room, Room> _corridorOf = new Dictionary<Room, Room>();
private LevelManager levelManager;
private static readonly ExitsInRoomType[] AllDirs =
@ -40,40 +44,153 @@ namespace _Project.Scripts.LevelGenerating
GenerateFloor();
}
bool PlaceElevator()
bool PlaceElevator()
{
while (true)
{
if (TryAttachLift(true, roomMargin)) return true; // норма: глубина >= минимума
if (TryAttachLift(false, roomMargin)) return true; // без требования глубины
if (TryAttachLift(false, 0f)) return true; // без зазора
if (!PruneRandomLeaf()) return false; // освобождаем место и повторяем
}
}
private ExitsInRoomType[] LiftEntryDirs()
{
if (lengthLiftRoom > widthLiftRoom) return new[] { ExitsInRoomType.ZAxisP, ExitsInRoomType.ZAxisN };
if (widthLiftRoom > lengthLiftRoom) return new[] { ExitsInRoomType.XAxisP, ExitsInRoomType.XAxisN };
return AllDirs;
}
// Сдвиги лифта вдоль стены, чтобы коридор всё равно попал в стену
private IEnumerable<int> LiftShifts(ExitsInRoomType dir)
{
yield return 0;
if (!liftDoorAnyPosition) yield break;
bool xDir = dir == ExitsInRoomType.XAxisP || dir == ExitsInRoomType.XAxisN;
int cross = xDir ? widthLiftRoom : lengthLiftRoom;
int max = (cross - corridorWigth) / 2;
for (int s = 1; s <= max; s++) { yield return s; yield return -s; }
}
bool TryAttachLift(bool requireMinDepth, float margin)
{
var start = levelManager.GetRooms().First(r => r.rt == RoomType.Start);
var parents = _depth.Keys
.OrderByDescending(r => _depth[r])
.ThenByDescending(r => (r.Position - start.Position).sqrMagnitude)
.ToList();
foreach (var parent in parents)
{
if (requireMinDepth && _depth[parent] + 1 < _minDepthLiftRoom) continue;
foreach (var dir in LiftEntryDirs().OrderBy(_ => Random.value))
{
var start = levelManager.GetRooms().First(r => r.rt == RoomType.Start);
if (parent.exits.Contains(dir)) continue;
// максимум шагов, при равенстве выбираем самую дальнюю от старта по прямой
var best = _depth
.OrderByDescending(p => p.Value)
.ThenByDescending(p => (p.Key.Position - start.Position).sqrMagnitude)
.First();
if (best.Key == start || best.Value <= _minDepthLiftRoom)
foreach (int shift in LiftShifts(dir))
{
return false;
Room lift = new Room { Length = lengthLiftRoom, Width = widthLiftRoom };
PlaceByEntry(EntryPoint(parent, dir), dir, lift);
bool xDir = dir == ExitsInRoomType.XAxisP || dir == ExitsInRoomType.XAxisN;
lift.Position += xDir ? new Vector2(0, shift) : new Vector2(shift, 0);
if (!CanPlaceLift(parent, lift, dir, margin)) continue;
lift.rt = RoomType.Lift;
lift.exits = new List<ExitsInRoomType>();
lift.entrance = ToEntrance(dir);
lift.isWallable = false;
levelManager.AddRoom(lift);
_depth[lift] = _depth[parent] + 1;
_parent[lift] = (parent, dir);
_corridorOf[lift] = CreateCorridor(parent, dir);
parent.exits.Add(dir);
_curRooms++;
return true;
}
best.Key.rt = RoomType.Lift; // добавь значение в enum RoomType
best.Key.Length = lengthLiftRoom;
best.Key.Width = widthLiftRoom;
best.Key.exits.Clear();
return true;
}
}
return false;
}
bool CanPlaceLift(Room parent, Room lift, ExitsInRoomType dir, float margin)
{
if (!InBounds(lift)) return false;
if (!IsAreaFree(CorridorRect(parent, dir))) return false;
Rect area = RoomRect(lift);
float m = Mathf.Min(margin, corridorLength);
area.xMin -= m; area.yMin -= m; area.xMax += m; area.yMax += m;
return IsAreaFree(area, parent);
}
// Удаляет случайную тупиковую комнату вместе с её коридором
bool PruneRandomLeaf()
{
var parentsSet = new HashSet<Room>(_parent.Values.Select(v => v.parent));
var leaves = _parent.Keys.Where(r => !parentsSet.Contains(r)).ToList();
if (leaves.Count == 0) return false;
Room leaf = leaves[Random.Range(0, leaves.Count)];
var (p, dir) = _parent[leaf];
LevelManager.Rooms.Remove(leaf);
if (_corridorOf.TryGetValue(leaf, out var cor))
{
LevelManager.Rooms.Remove(cor);
_corridorOf.Remove(leaf);
}
p.exits.Remove(dir);
_depth.Remove(leaf);
_parent.Remove(leaf);
_curRooms--;
return true;
}
// Позиция комнаты заданного размера относительно точки входа
private static void PlaceByEntry(Vector2 e, ExitsInRoomType dir, Room n)
{
switch (dir)
{
case ExitsInRoomType.XAxisP: n.Position = new Vector2(e.x, e.y - n.Width / 2f); break;
case ExitsInRoomType.XAxisN: n.Position = new Vector2(e.x - n.Length, e.y - n.Width / 2f); break;
case ExitsInRoomType.ZAxisP: n.Position = new Vector2(e.x - n.Length / 2f, e.y); break;
default: n.Position = new Vector2(e.x - n.Length / 2f, e.y - n.Width); break;
}
}
private bool InBounds(Room r) =>
r.Position.x >= 0 && r.Position.y >= 0 &&
r.Position.x + r.Length <= lengthFloor &&
r.Position.y + r.Width <= lengthFloor;
// Комнаты проверяются с зазором, коридоры без него (иначе зазор заденет собственный коридор лифта)
void GenerateFloor()
{
LevelManager.Rooms.Clear();
_depth.Clear();// статический список мог остаться с прошлого запуска
_curRooms = 0;
const int maxAttempts = 50;
bool ok = false;
CreateStartRoom();
if (!PlaceElevator())
for (int i = 0; i < maxAttempts && !ok; i++)
{
GenerateFloor();
LevelManager.Rooms.Clear();
_depth.Clear();
_parent.Clear();
_corridorOf.Clear();
_curRooms = 0;
CreateStartRoom();
ok = PlaceElevator();
}
if (!ok) Debug.LogWarning("Лифт не удалось разместить, этаж собран без него");
Instantiate(GameObjectA, new Vector3(0, -1, 0), Quaternion.identity);
foreach (var room in levelManager.GetRooms())
@ -243,7 +360,8 @@ namespace _Project.Scripts.LevelGenerating
// Всё хорошо - фиксируем
levelManager.AddRoom(newRoom);
_depth[newRoom] = _depth[prevRoom] + 1;
CreateCorridor(prevRoom, dir);
_parent[newRoom] = (prevRoom, dir);
_corridorOf[newRoom] = CreateCorridor(prevRoom, dir); // вместо отдельного CreateCorridor(...)
_curRooms++;
if (!prevRoom.exits.Contains(dir)) prevRoom.exits.Add(dir);