вроде порешал косяк лифта с поворотами. Мб не работает
This commit is contained in:
parent
490c534cfa
commit
9519be076f
@ -13842,15 +13842,15 @@ MonoBehaviour:
|
|||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::_Project.Scripts.LevelGenerating.WallsGenerator
|
m_EditorClassIdentifier: Assembly-CSharp::_Project.Scripts.LevelGenerating.WallsGenerator
|
||||||
floorsCount: 10
|
floorsCount: 10
|
||||||
lengthWallMin: 10
|
lengthWallMin: 20
|
||||||
lengthWallMax: 25
|
lengthWallMax: 40
|
||||||
lengthStartRoom: 10
|
lengthStartRoom: 10
|
||||||
lengthLiftRoom: 10
|
lengthLiftRoom: 10
|
||||||
widthLiftRoom: 5
|
widthLiftRoom: 5
|
||||||
lengthFloor: 100
|
lengthFloor: 100
|
||||||
corridorLength: 5
|
corridorLength: 5
|
||||||
corridorWigth: 4
|
corridorWigth: 4
|
||||||
_minDepthLiftRoom: 0
|
_minDepthLiftRoom: 7
|
||||||
GameObjectA: {fileID: 8066171868259172852, guid: e46748b8eb8a6074d9bb87701cebe84b, type: 3}
|
GameObjectA: {fileID: 8066171868259172852, guid: e46748b8eb8a6074d9bb87701cebe84b, type: 3}
|
||||||
GameObjectB: {fileID: 4641718306729093824, guid: 3905eab8a5f6b3049a83378b1acf1ebc, type: 3}
|
GameObjectB: {fileID: 4641718306729093824, guid: 3905eab8a5f6b3049a83378b1acf1ebc, type: 3}
|
||||||
maxRooms: 15
|
maxRooms: 15
|
||||||
|
|||||||
@ -26,6 +26,10 @@ namespace _Project.Scripts.LevelGenerating
|
|||||||
|
|
||||||
private int _curRooms;
|
private int _curRooms;
|
||||||
private readonly Dictionary<Room, int> _depth = new Dictionary<Room, int>();
|
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 LevelManager levelManager;
|
||||||
|
|
||||||
private static readonly ExitsInRoomType[] AllDirs =
|
private static readonly ExitsInRoomType[] AllDirs =
|
||||||
@ -40,40 +44,153 @@ namespace _Project.Scripts.LevelGenerating
|
|||||||
GenerateFloor();
|
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;
|
||||||
|
|
||||||
// максимум шагов, при равенстве выбираем самую дальнюю от старта по прямой
|
foreach (int shift in LiftShifts(dir))
|
||||||
var best = _depth
|
|
||||||
.OrderByDescending(p => p.Value)
|
|
||||||
.ThenByDescending(p => (p.Key.Position - start.Position).sqrMagnitude)
|
|
||||||
.First();
|
|
||||||
|
|
||||||
if (best.Key == start || best.Value <= _minDepthLiftRoom)
|
|
||||||
{
|
{
|
||||||
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()
|
void GenerateFloor()
|
||||||
{
|
{
|
||||||
LevelManager.Rooms.Clear();
|
const int maxAttempts = 50;
|
||||||
_depth.Clear();// статический список мог остаться с прошлого запуска
|
bool ok = false;
|
||||||
_curRooms = 0;
|
|
||||||
|
|
||||||
CreateStartRoom();
|
for (int i = 0; i < maxAttempts && !ok; i++)
|
||||||
|
|
||||||
if (!PlaceElevator())
|
|
||||||
{
|
{
|
||||||
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);
|
Instantiate(GameObjectA, new Vector3(0, -1, 0), Quaternion.identity);
|
||||||
|
|
||||||
foreach (var room in levelManager.GetRooms())
|
foreach (var room in levelManager.GetRooms())
|
||||||
@ -243,7 +360,8 @@ namespace _Project.Scripts.LevelGenerating
|
|||||||
// Всё хорошо - фиксируем
|
// Всё хорошо - фиксируем
|
||||||
levelManager.AddRoom(newRoom);
|
levelManager.AddRoom(newRoom);
|
||||||
_depth[newRoom] = _depth[prevRoom] + 1;
|
_depth[newRoom] = _depth[prevRoom] + 1;
|
||||||
CreateCorridor(prevRoom, dir);
|
_parent[newRoom] = (prevRoom, dir);
|
||||||
|
_corridorOf[newRoom] = CreateCorridor(prevRoom, dir); // вместо отдельного CreateCorridor(...)
|
||||||
_curRooms++;
|
_curRooms++;
|
||||||
if (!prevRoom.exits.Contains(dir)) prevRoom.exits.Add(dir);
|
if (!prevRoom.exits.Contains(dir)) prevRoom.exits.Add(dir);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user