484 lines
21 KiB
C#
484 lines
21 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel.Design.Serialization;
|
||
using System.Linq;
|
||
using UnityEngine;
|
||
using Random = UnityEngine.Random;
|
||
|
||
namespace _Project.Scripts.LevelGenerating
|
||
{
|
||
public class WallsGenerator : MonoBehaviour
|
||
{
|
||
|
||
[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;
|
||
GenerateFloor();
|
||
}
|
||
|
||
void GenerateFloor()
|
||
{
|
||
CreateStartRoom();
|
||
Instantiate(GameObjectA, new Vector3(0,-1,0), Quaternion.identity);
|
||
foreach (var room in levelManager.GetRooms())
|
||
{
|
||
|
||
float x = room.Position.x;
|
||
float z = room.Position.y; // В Vector2 ось Y отвечает за Z в 3D пространстве Unity
|
||
float length = room.Length;
|
||
float width = room.Width;
|
||
|
||
// 4 угла комнаты:
|
||
// 1. Нижний левый (X, Z)
|
||
Instantiate(GameObjectB, new Vector3(x, 0, z), Quaternion.identity);
|
||
// 2. Нижний правый (X + Length, Z)
|
||
Instantiate(GameObjectB, new Vector3(x + length, 0, z), Quaternion.identity);
|
||
// 3. Верхний правый (X + Length, Z + Width)
|
||
Instantiate(GameObjectB, new Vector3(x + length, 0, z + width), Quaternion.identity);
|
||
// 4. Верхний левый (X, Z + Width)
|
||
Instantiate(GameObjectB, new Vector3(x, 0, z + width), Quaternion.identity);
|
||
}
|
||
|
||
}
|
||
|
||
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 prevRoom,ExitsInRoomType exitForCor)
|
||
{
|
||
if (prevRoom == null || prevRoom.exits.Count == 0) return;
|
||
|
||
// if (IsRoomCanFit(new Vector2(prevRoom.Position.x + prevRoom.Length, prevRoom.Position.y / 2),
|
||
// exitForCor))
|
||
// {
|
||
// Room newRoom = GenerateRandomRoom();
|
||
// newRoom.Position = new Vector2();
|
||
// newRoom.rt = RoomType.Undefined;
|
||
// newRoom.entrance = EntranceToRoomType.XAxisP;
|
||
// newRoom.exits = new List<ExitsInRoomType>();
|
||
// if (IsRoomCanFit(new Vector2(prevRoom.Position.x + prevRoom.Length, prevRoom.Position.y / 2),
|
||
// newRoom.Length, newRoom.Width, exitForCor))
|
||
// {
|
||
// newRoom.isWallable = false;
|
||
// levelManager.AddRoom(newRoom);
|
||
// CreateCorridor(newRoom.Position, newRoom.Length, newRoom.Width, exitForCor);
|
||
// }
|
||
// else
|
||
// {
|
||
// newRoom.isWallable = true;
|
||
// EditRoomFoFitting(newRoom);
|
||
// levelManager.AddRoom(newRoom);
|
||
// CreateCorridor(newRoom.Position, newRoom.Length, newRoom.Width, exitForCor);
|
||
// }
|
||
// }
|
||
|
||
|
||
Room newRoom = GenerateRandomRoom();
|
||
newRoom.rt = RoomType.Undefined;
|
||
newRoom.exits = new List<ExitsInRoomType>();
|
||
|
||
Vector2 newRoomPos = Vector2.zero;
|
||
|
||
// Вычисляем позицию новой комнаты и откуда в нее входит коридор
|
||
switch (exitForCor)
|
||
{
|
||
// Если вышли направо, новая комната слева получает вход (-X)
|
||
case ExitsInRoomType.XAxisP:
|
||
newRoom.entrance = EntranceToRoomType.XAxisN;
|
||
newRoomPos = new Vector2(
|
||
prevRoom.Position.x + prevRoom.Length + corridorLength, // Сдвигаем на ширину пред. комнаты и коридор
|
||
prevRoom.Position.y + (prevRoom.Width / 2f) - (newRoom.Width / 2f) // Центрируем по оси Y (Z)
|
||
);
|
||
break;
|
||
|
||
// Если вышли налево
|
||
case ExitsInRoomType.XAxisN:
|
||
newRoom.entrance = EntranceToRoomType.XAxisP;
|
||
newRoomPos = new Vector2(
|
||
prevRoom.Position.x - corridorLength - newRoom.Length, // Отступаем влево на коридор и ширину новой комнаты
|
||
prevRoom.Position.y + (prevRoom.Width / 2f) - (newRoom.Width / 2f)
|
||
);
|
||
break;
|
||
|
||
// Если вышли вверх (+Z/Y)
|
||
case ExitsInRoomType.ZAxisP:
|
||
newRoom.entrance = EntranceToRoomType.ZAxisN;
|
||
newRoomPos = new Vector2(
|
||
prevRoom.Position.x + (prevRoom.Length / 2f) - (newRoom.Length / 2f), // Центрируем по оси X
|
||
prevRoom.Position.y + prevRoom.Width + corridorLength
|
||
);
|
||
break;
|
||
|
||
// Если вышли вниз (-Z/Y)
|
||
case ExitsInRoomType.ZAxisN:
|
||
newRoom.entrance = EntranceToRoomType.ZAxisP;
|
||
newRoomPos = new Vector2(
|
||
prevRoom.Position.x + (prevRoom.Length / 2f) - (newRoom.Length / 2f),
|
||
prevRoom.Position.y - corridorLength - newRoom.Width
|
||
);
|
||
break;
|
||
}
|
||
|
||
// Устанавливаем просчитанную позицию
|
||
newRoom.Position = newRoomPos;
|
||
|
||
// Проверяем, помещается ли комната с ее реальными размерами
|
||
if (IsRoomCanFit(newRoom.Position, newRoom.Length, newRoom.Width, exitForCor))
|
||
{
|
||
newRoom.isWallable = false;
|
||
levelManager.AddRoom(newRoom);
|
||
CreateCorridor(newRoom.Position, newRoom.Length, newRoom.Width, exitForCor);
|
||
}
|
||
else
|
||
{
|
||
newRoom.isWallable = true;
|
||
EditRoomFoFitting(newRoom); // Подгоняем размеры под границы карты
|
||
|
||
// Проверяем, вмещается ли хотя бы комната минимального размера после обрезки
|
||
if (newRoom.Length >= lengthWallMin && newRoom.Width >= lengthWallMin)
|
||
{
|
||
levelManager.AddRoom(newRoom);
|
||
CreateCorridor(newRoom.Position, newRoom.Length, newRoom.Width, exitForCor);
|
||
}
|
||
else
|
||
{
|
||
// Места не хватило даже для минимальной комнаты.
|
||
// Отменяем создание этой комнаты и завершаем ветку.
|
||
// При необходимости здесь можно удалить сгенерированный к ней коридор.
|
||
return;
|
||
}
|
||
}
|
||
if (exitForCor != ExitsInRoomType.XAxisN) TryPlaceRoom(newRoom, ExitsInRoomType.XAxisP);
|
||
|
||
// 2. Влево (-X): идем, только если TryPlaceRoomНЕ слеа (XAxisP)
|
||
if (exitForCor != ExitsInRoomType.XAxisP) TryPlaceRoom(newRoom, ExitsInRoomType.XAxisN);
|
||
|
||
// 3. Вверх/Вперед (+Z): идем, только TryPlaceRoomпришли НЕ верху (ZAxisN)
|
||
if (exitForCor != ExitsInRoomType.ZAxisN) TryPlaceRoom(newRoom, ExitsInRoomType.ZAxisP);
|
||
|
||
// 4. Вниз/Назад (-Z): идем, только если TryPlaceRoomишли НЕ сниу (ZAxisP)
|
||
if (exitForCor != ExitsInRoomType.ZAxisP) TryPlaceRoom(newRoom, ExitsInRoomType.ZAxisN);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
private void EditRoomFoFitting(Room room)
|
||
{
|
||
if (lengthFloor - room.Position.x + room.Length <= 0)
|
||
{
|
||
room.Length = lengthFloor - Convert.ToInt32(room.Position.x);
|
||
}
|
||
if (lengthFloor - room.Position.y + room.Width <= 0)
|
||
{
|
||
room.Width = lengthFloor - Convert.ToInt32(room.Position.y);
|
||
}
|
||
|
||
if (room.Position.x < 0)
|
||
{
|
||
room.Position.x = 0;
|
||
}
|
||
|
||
if (room.Position.y < 0)
|
||
{
|
||
room.Position.y = 0;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
private bool IsRoomCanFit(Vector2 vector2, ExitsInRoomType exitForCor)
|
||
{
|
||
switch (exitForCor)
|
||
{
|
||
case ExitsInRoomType.XAxisP:
|
||
{
|
||
if ((lengthFloor - (vector2.x + lengthWallMin) >= 0)
|
||
&& (lengthFloor - (vector2.y + lengthWallMin / 2f) >= 0)
|
||
&& (vector2.y - lengthWallMin/ 2f) >= 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
case ExitsInRoomType.XAxisN:
|
||
{
|
||
if ((vector2.x - lengthWallMin >= 0)
|
||
&& (lengthFloor - (vector2.y + lengthWallMin / 2f) >= 0)
|
||
&& (vector2.y - lengthWallMin/ 2f) >= 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
case ExitsInRoomType.ZAxisP:
|
||
{
|
||
if ((lengthFloor - (vector2.y + lengthWallMin) >= 0)
|
||
&& (lengthFloor - (vector2.x + lengthWallMin / 2f) >= 0)
|
||
&& (vector2.x - lengthWallMin/ 2f) >= 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
case ExitsInRoomType.ZAxisN:
|
||
{
|
||
if ((vector2.y - lengthWallMin >= 0)
|
||
&& (lengthFloor - (vector2.x + lengthWallMin / 2f) >= 0)
|
||
&& (vector2.x - lengthWallMin/ 2f) >= 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
case ExitsInRoomType.Null:
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
private bool IsRoomCanFit(Vector2 vector2,int lengthX,int lengthZ, ExitsInRoomType exitForCor)
|
||
{
|
||
switch (exitForCor)
|
||
{
|
||
case ExitsInRoomType.XAxisP:
|
||
{
|
||
if ((lengthFloor - (vector2.x + lengthX) >= 0)
|
||
&& (lengthFloor - (vector2.y + lengthZ / 2f) >= 0)
|
||
&& (vector2.y - lengthZ/2f) >= 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
case ExitsInRoomType.XAxisN:
|
||
{
|
||
if ((vector2.x - lengthX >= 0)
|
||
&& (lengthFloor - (vector2.y + lengthZ / 2f) >= 0)
|
||
&& (vector2.y - lengthZ/2f) >= 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
case ExitsInRoomType.ZAxisP:
|
||
{
|
||
if ((lengthFloor - (vector2.y + lengthZ) >= 0)
|
||
&& (lengthFloor - (vector2.x + lengthX / 2f) >= 0)
|
||
&& (vector2.x - lengthX/2f) >= 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
case ExitsInRoomType.ZAxisN:
|
||
{
|
||
if ((vector2.y - lengthZ >= 0)
|
||
&& (lengthFloor - (vector2.x + lengthX / 2f) >= 0)
|
||
&& (vector2.x - lengthX/2) >= 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
case ExitsInRoomType.Null:
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
Room GenerateRandomRoom()
|
||
{
|
||
int a = Random.Range(lengthWallMin, lengthWallMax);
|
||
int b = Random.Range(lengthWallMin, lengthWallMax);
|
||
Room room = new Room();
|
||
room.Length = a;
|
||
room.Width = 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(room,room.exits.First());
|
||
}
|
||
}
|
||
|
||
}
|
||
} |