Souljack/Assets/_Project/Scripts/LevelGenerating/WallsGenerator.cs
2026-10-01 18:06:35 +03:00

345 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using System.ComponentModel.Design.Serialization;
using System.Linq;
using UnityEngine;
namespace _Project.Scripts.LevelGenerating
{
public class WallsGenerator : MonoBehaviour
{
[SerializeField] private int roomCountMin;
[SerializeField] private int roomCountMax;
[SerializeField] private int floorsCount;
[SerializeField] private int lengthWallMin;
[SerializeField] private int lengthWallMax;
[SerializeField] private int lengthStartRoom;
[SerializeField] private int lengthFloor;
[SerializeField] private int corridorLength;
[SerializeField] private int corridorWigth;
[SerializeField] private GameObject GameObjectA;
[SerializeField] private GameObject GameObjectB;
private int RoomCount;
private LevelManager levelManager;
void Start()
{
levelManager = LevelManager.Instance;
CreateStartRoom();
}
void GenerateRooms()
{
RoomCount = Random.Range(roomCountMin, roomCountMax);
LevelManager.Rooms.Add(new Room(Vector2.zero,lengthStartRoom, lengthStartRoom,RoomType.Start));
}
Room CreateCorridor(Vector2 posRoom, int Length, int Wigth, ExitsInRoomType exitType)
{
Vector2 corridorPos = Vector2.zero;
int cLength = 0;
int cWidth = 0;
EntranceToRoomType entranceToRoomType = EntranceToRoomType.Null;
List<ExitsInRoomType> exitsInRoomType = new List<ExitsInRoomType>();
switch (exitType)
{
// Выход направо (+X)
case ExitsInRoomType.XAxisP:
// Начинается у правой стены комнаты (posRoom.x + Length)
// Центрируем коридор по высоте/оси Z: смещаем на половину разницы ширин
exitsInRoomType.Add (ExitsInRoomType.XAxisP);
entranceToRoomType = EntranceToRoomType.XAxisN;
corridorPos = new Vector2(
posRoom.x + Length,
posRoom.y + (Wigth - corridorWigth) / 2f
);
cLength = corridorLength;
cWidth = corridorWigth;
break;
// Выход налево (-X)
case ExitsInRoomType.XAxisN:
// Начинается слева от комнаты, отступая на длину коридора
exitsInRoomType .Add (ExitsInRoomType.XAxisN);
entranceToRoomType = EntranceToRoomType.XAxisP;
corridorPos = new Vector2(
posRoom.x - corridorLength,
posRoom.y + (Wigth - corridorWigth) / 2f
);
cLength = corridorLength;
cWidth = corridorWigth;
break;
// Выход вперед/вверх (+Z / в Vector2 это Y)
case ExitsInRoomType.ZAxisP:
entranceToRoomType = EntranceToRoomType.ZAxisN;
exitsInRoomType.Add (ExitsInRoomType.ZAxisP);
// Начинается у верхней стены комнаты (posRoom.y + Wigth)
// Центрируем коридор по горизонтали/оси X:
corridorPos = new Vector2(
posRoom.x + (Length - corridorWigth) / 2f,
posRoom.y + Wigth
);
// По оси X коридор узкий, по оси Z — длинный:
cLength = corridorWigth;
cWidth = corridorLength;
break;
// Выход назад/вниз (-Z / в Vector2 это Y)
case ExitsInRoomType.ZAxisN:
entranceToRoomType = EntranceToRoomType.ZAxisP;
exitsInRoomType.Add (ExitsInRoomType.ZAxisN);
// Начинается ниже комнаты, отступая на длину коридора
corridorPos = new Vector2(
posRoom.x + (Length - corridorWigth) / 2f,
posRoom.y - corridorLength
);
cLength = corridorWigth;
cWidth = corridorLength;
break;
}
// Создаем коридор как отдельную комнату
Room corridor = new Room(corridorPos,
cLength, cWidth, RoomType.Corridor,false,
EntranceToRoomType.Null,exitsInRoomType);
LevelManager.Rooms.Add(corridor);
// Визуализация коридора (расставляем маркеры по углам коридора для наглядности)
Instantiate(GameObjectB, new Vector3(corridorPos.x, 0, corridorPos.y), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(corridorPos.x + cLength, 0, corridorPos.y), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(corridorPos.x + cLength, 0, corridorPos.y + cWidth), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(corridorPos.x, 0, corridorPos.y + cWidth), Quaternion.identity);
return corridor;
}
void TryPlaceRoom(Room corridor)
{
// Базовая защита: если коридора нет или у него нет направления выхода
if (corridor == null || corridor.exits.Count == 0) return;
ExitsInRoomType corridorDirection = corridor.exits.Last();
// Создаем новую комнату со случайными размерами
Room newRoom = GenerateRandomRoom();
newRoom.rt = RoomType.Undefined;
Vector2 roomPos = Vector2.zero;
EntranceToRoomType entrance = EntranceToRoomType.Null;
// 1. Стыкуем вход новой комнаты с выходом из коридора
switch (corridorDirection)
{
case ExitsInRoomType.XAxisP: // Коридор шел вправо (+X)
roomPos = new Vector2(
corridor.Position.x + corridor.Length,
corridor.Position.y + (corridor.Wigth - newRoom.Wigth) / 2f
);
entrance = EntranceToRoomType.XAxisN; // Вход в комнату с левой стены (-X)
break;
case ExitsInRoomType.XAxisN: // Коридор шел влево (-X)
roomPos = new Vector2(
corridor.Position.x - newRoom.Length,
corridor.Position.y + (corridor.Wigth - newRoom.Wigth) / 2f
);
entrance = EntranceToRoomType.XAxisP; // Вход с правой стены (+X)
break;
case ExitsInRoomType.ZAxisP: // Коридор шел вверх (+Z)
roomPos = new Vector2(
corridor.Position.x + (corridor.Length - newRoom.Length) / 2f,
corridor.Position.y + corridor.Wigth
);
entrance = EntranceToRoomType.ZAxisN; // Вход с нижней стены (-Z)
break;
case ExitsInRoomType.ZAxisN: // Коридор шел вниз (-Z)
roomPos = new Vector2(
corridor.Position.x + (corridor.Length - newRoom.Length) / 2f,
corridor.Position.y - newRoom.Wigth
);
entrance = EntranceToRoomType.ZAxisP; // Вход с верхней стены (+Z)
break;
}
// 2. Проверяем: влезает ли комната в границы и не накладывается ли на другие комнаты/коридоры
if (!IsAreaClear(roomPos, newRoom.Length, newRoom.Wigth))
{
// Не влезла — эта ветка лабиринта затухает сама
return;
}
// Если всё чисто, сохраняем параметры и добавляем в список
newRoom.Position = roomPos;
newRoom.entrance = entrance;
LevelManager.Rooms.Add(newRoom);
// Визуализация новой комнаты (маркеры по углам)
Instantiate(GameObjectB, new Vector3(newRoom.Position.x, 0, newRoom.Position.y), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(newRoom.Position.x + newRoom.Length, 0, newRoom.Position.y), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(newRoom.Position.x + newRoom.Length, 0, newRoom.Position.y + newRoom.Wigth), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(newRoom.Position.x, 0, newRoom.Position.y + newRoom.Wigth), Quaternion.identity);
// 3. РЕКУРСИЯ: получаем 3 свободные стены (все кроме той, где вход)
List<ExitsInRoomType> availableSides = GetThreeAvailableSides(entrance);
// Перемешиваем стороны, чтобы ветвление шло в случайном порядке
availableSides = availableSides.OrderBy(x => Random.value).ToList();
foreach (var exitSide in availableSides)
{
// Шанс ветвления 65%: чтобы этаж не забивался сплошным ковром комнат,
// а оставались тупики и коридорные развилки
if (Random.value < 0.65f)
{
// Пытаемся построить коридор в эту сторону
Room nextCorridor = CreateCorridor(newRoom.Position, newRoom.Length, newRoom.Wigth, exitSide);
if (nextCorridor != null)
{
// Рекурсивный шаг: пытаемся пристроить комнату к концу нового коридора
TryPlaceRoom(nextCorridor);
}
}
}
}
// Вспомогательный метод 1: возвращает 3 стены, исключая стену входа
List<ExitsInRoomType> GetThreeAvailableSides(EntranceToRoomType entrance)
{
List<ExitsInRoomType> sides = new List<ExitsInRoomType>();
if (entrance != EntranceToRoomType.XAxisN) sides.Add(ExitsInRoomType.XAxisN);
if (entrance != EntranceToRoomType.XAxisP) sides.Add(ExitsInRoomType.XAxisP);
if (entrance != EntranceToRoomType.ZAxisN) sides.Add(ExitsInRoomType.ZAxisN);
if (entrance != EntranceToRoomType.ZAxisP) sides.Add(ExitsInRoomType.ZAxisP);
return sides;
}
// Вспомогательный метод 2: проверка на границы этажа и пересечение с существующими объектами
bool IsAreaClear(Vector2 pos, int length, int wigth)
{
// Проверка границ этажа [0 .. lengthFloor]
if (pos.x < 0 || pos.y < 0 ||
pos.x + length > lengthFloor ||
pos.y + wigth > lengthFloor)
{
return false;
}
Rect newRect = new Rect(pos.x, pos.y, length, wigth);
// Проверка наложения на все уже созданные комнаты и коридоры
foreach (var existing in LevelManager.Rooms)
{
Rect existingRect = new Rect(existing.Position.x, existing.Position.y, existing.Length, existing.Wigth);
if (newRect.Overlaps(existingRect))
{
return false;
}
}
return true;
}
Room GenerateRandomRoom()
{
int a = Random.Range(lengthWallMin, lengthWallMax);
int b = Random.Range(lengthWallMin, lengthWallMax);
Room room = new Room();
room.Length = a;
room.Wigth = b;
return room;
}
void CreateStartRoom()
{
float randValue = Random.value;
int xR, zR;
Vector2Int posStartRoom = new Vector2Int();
Room room = new Room( lengthStartRoom, lengthStartRoom, RoomType.Start);
if (randValue < 0.25f) //1 квадрант
{
xR = Random.Range(0,lengthFloor/2-lengthStartRoom);
if (xR + lengthStartRoom <= lengthFloor / 4)
{
room.exits.Add(ExitsInRoomType.XAxisP);
zR = Random.Range(0, lengthFloor/2-lengthStartRoom);
}
else
{
room.exits.Add(ExitsInRoomType.ZAxisP);
zR = Random.Range(0, lengthFloor/4-lengthStartRoom);
}
}
else if (randValue < 0.5f) // 2 квадрант
{
xR = Random.Range(lengthFloor/2, lengthFloor-lengthStartRoom);//от половины до конца
if (xR + lengthStartRoom <= lengthFloor * 3/4)
{
room.exits.Add(ExitsInRoomType.ZAxisP);
zR = Random.Range(0, lengthFloor/4-lengthStartRoom);
}
else
{
room.exits.Add(ExitsInRoomType.XAxisN);
zR = Random.Range(0, lengthFloor/2-lengthStartRoom);
}
}
else if (randValue < 0.75f)
{
xR = Random.Range(lengthFloor / 2, lengthFloor - lengthStartRoom);
if (xR + lengthStartRoom <= lengthFloor * 3 / 4)
{
room.exits.Add(ExitsInRoomType.ZAxisN);
zR = Random.Range(lengthFloor * 3 / 4, lengthFloor - lengthStartRoom);
}
else
{
room.exits.Add(ExitsInRoomType.XAxisN);
zR = Random.Range(lengthFloor / 2, lengthFloor - lengthStartRoom);
}
}
else
{
xR = Random.Range(0, lengthFloor / 2 - lengthStartRoom);
if (xR + lengthStartRoom <= lengthFloor / 4)
{
room.exits.Add(ExitsInRoomType.XAxisP);
zR = Random.Range(lengthFloor / 2, lengthFloor - lengthStartRoom);
}
else
{
room.exits.Add(ExitsInRoomType.ZAxisN);
zR = Random.Range(lengthFloor * 3 / 4, lengthFloor - lengthStartRoom);
}
}
room.entrance = EntranceToRoomType.Null;
LevelManager.Rooms.Add(room);
Instantiate(GameObjectA, new Vector3(0,-1,0), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(xR,0,zR), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(xR+lengthStartRoom,0,zR), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(xR+lengthStartRoom,0,zR+lengthStartRoom), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(xR,0,zR+lengthStartRoom), Quaternion.identity);
CreateCorridor(new Vector2Int(xR, zR),lengthStartRoom,lengthStartRoom,room.exits.First());
Room firstCorridor = CreateCorridor(new Vector2(xR, zR), lengthStartRoom, lengthStartRoom, room.exits.First());
if (firstCorridor != null)
{
TryPlaceRoom(firstCorridor);
}
}
}
}