готовый генератор комнат

This commit is contained in:
vladislove 2026-10-09 01:41:25 +03:00
parent 9519be076f
commit 650af515e9
12 changed files with 770 additions and 273 deletions

6
.vsconfig Normal file
View File

@ -0,0 +1,6 @@
{
"version": "1.0",
"components": [
"Microsoft.VisualStudio.Workload.ManagedGame"
]
}

View File

@ -10550,7 +10550,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!4 &552049569
Transform:
m_ObjectHideFlags: 0
@ -13843,19 +13843,22 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::_Project.Scripts.LevelGenerating.WallsGenerator
floorsCount: 10
lengthWallMin: 20
lengthWallMax: 40
lengthWallMax: 70
lengthStartRoom: 10
lengthLiftRoom: 10
widthLiftRoom: 5
lengthFloor: 100
corridorLength: 5
corridorWigth: 4
_minDepthLiftRoom: 7
minRoomCount: 7
_minDepthLiftRoom: 5
_minDistanceStartLift: 50
GameObjectA: {fileID: 8066171868259172852, guid: e46748b8eb8a6074d9bb87701cebe84b, type: 3}
GameObjectB: {fileID: 4641718306729093824, guid: 3905eab8a5f6b3049a83378b1acf1ebc, type: 3}
maxRooms: 15
branchChance: 0.7
roomMargin: 2
liftDoorAnyPosition: 1
--- !u!4 &740097205
Transform:
m_ObjectHideFlags: 0
@ -41319,7 +41322,7 @@ Transform:
m_GameObject: {fileID: 1033791869251556189}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 3, z: 0}
m_LocalPosition: {x: 0, y: 10, z: 0}
m_LocalScale: {x: 0.5, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:

View File

@ -0,0 +1,139 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-1320640688500226478
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: New Material 2
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 2000
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _ScreenSpaceReflections: 1
- _ScreenSpaceReflectionsContributeTransparent: 1
- _Smoothness: 1
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.053014517, g: 1, b: 0, a: 1}
- _Color: {r: 0.053014494, g: 1, b: 0, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b4555641771ab6d4c81ed310ee2dc560
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,146 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &3836052751326043178
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7087884702110966620}
- component: {fileID: 69987631208049368}
- component: {fileID: 3695676037745970672}
- component: {fileID: 6869080578825851964}
m_Layer: 3
m_Name: Cube
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &7087884702110966620
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3836052751326043178}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0.5, y: 0, z: 0.5}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 610399867894923410}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &69987631208049368
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3836052751326043178}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &3695676037745970672
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3836052751326043178}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: b4555641771ab6d4c81ed310ee2dc560, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!65 &6869080578825851964
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3836052751326043178}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &4641718306729093824
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 610399867894923410}
m_Layer: 0
m_Name: GameObjectC
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &610399867894923410
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4641718306729093824}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 10, y: 7.01, z: -0.52}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 1
m_Children:
- {fileID: 7087884702110966620}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 222a3fec38ee4e94da1bdcccb5c71aae
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,89 @@
using System.Collections.Generic;
using System.Linq;
namespace _Project.Scripts.LevelGenerating
{
public enum FloorKind
{
Generated, // обычный этаж: раскладка -> роли -> стены -> наполнение
Special // готовая локация (крыша с боссом), собрана вручную, генератор не нужен
}
// =====================================================================
// Данные ОДНОГО этажа. Заменяет статический LevelManager.Rooms.
// Все координаты и размеры комнат - в МОДУЛЯХ (1 модуль = 0.5 м), см. LevelGrid.
//
// НЕ ЗАБЫТЬ:
// - Для сохранения в файл этот класс НЕ годится напрямую: Room использует свойства,
// которые JsonUtility не сериализует. Для диска нужны отдельные структуры (FloorSave)
// с публичными полями, а FloorData остаётся рабочим объектом генерации.
// - Parent / CorridorOf живут в WallsGenerator (нужны только при генерации раскладки).
// =====================================================================
public class FloorData
{
public int Index; // номер этажа (0-based)
public FloorKind Kind;
public int Seed; // пока не используется (сид "пох"), поле на будущее
public string SpecialId; // для Special: id префаба локации, например "BossRoof"
public int SizeModules; // сторона карты этажа в модулях (для Generated)
public readonly List<Room> Rooms = new List<Room>();
public readonly Dictionary<Room, int> Depth = new Dictionary<Room, int>();
public Room Start; // стартовая комната (вход с предыдущего этажа)
public Room Lift; // комната с лифтом (выход на следующий этаж)
// ---------- Выборки ----------
public IEnumerable<Room> Corridors => Rooms.Where(r => r.rt == RoomType.Corridor);
// Всё, кроме коридоров (старт, лифт и обычные комнаты)
public IEnumerable<Room> PlayableRooms => Rooms.Where(r => r.rt != RoomType.Corridor);
// Комнаты, которым ещё не назначена роль
public IEnumerable<Room> UnassignedRooms => Rooms.Where(r => r.rt == RoomType.Undefined);
public IEnumerable<Room> OfType(RoomType type) => Rooms.Where(r => r.rt == type);
public int GetDepth(Room room) => Depth.TryGetValue(room, out int d) ? d : -1;
// ---------- Изменение ----------
// Добавить комнату (не коридор) вместе с глубиной
public void AddRoom(Room room, int depth)
{
Rooms.Add(room);
Depth[room] = depth;
if (room.rt == RoomType.Start) Start = room;
else if (room.rt == RoomType.Lift) Lift = room;
}
// Коридоры глубины не имеют
public void AddCorridor(Room corridor) => Rooms.Add(corridor);
public void Remove(Room room)
{
Rooms.Remove(room);
Depth.Remove(room);
if (Start == room) Start = null;
if (Lift == room) Lift = null;
}
public void Clear()
{
Rooms.Clear();
Depth.Clear();
Start = null;
Lift = null;
}
// ---------- Фабрики ----------
public static FloorData CreateGenerated(int index, int sizeModules) =>
new FloorData { Index = index, Kind = FloorKind.Generated, SizeModules = sizeModules };
public static FloorData CreateSpecial(int index, string specialId) =>
new FloorData { Index = index, Kind = FloorKind.Special, SpecialId = specialId };
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c44c2a7978314fbeb585ae58fa82d95d
timeCreated: 1791498732

View File

@ -3,9 +3,31 @@ using UnityEngine;
namespace _Project.Scripts.LevelGenerating
{
// =====================================================================
// НЕ ЗАБЫТЬ (напоминание себе):
// Все координаты и размеры в Room хранятся в МОДУЛЯХ (1 модуль = 0.5 м), а не в метрах.
// - В инспекторе WallsGenerator размеры задаются в метрах и переводятся в модули (x2).
// - В мировые координаты переводить ТОЛЬКО через LevelGrid.ToWorld(...).
// - Размеры комнат всегда ЧЁТНЫЕ в модулях (целые метры), иначе центровка по коридору
// даст дробные позиции. Это обеспечивает WallsGenerator (метод Even).
// =====================================================================
public static class LevelGrid
{
public const float Module = 0.5f;
public const int PerMeter = 2;
public static int FromMeters(int meters) => meters * PerMeter;
public static Vector3 ToWorld(Vector2Int cell, float y = 0f) =>
new Vector3(cell.x * Module, y, cell.y * Module);
public static Vector3 ToWorld(Vector2 cell, float y = 0f) =>
new Vector3(cell.x * Module, y, cell.y * Module);
}
public class Room
{
public Room(Vector2 position, int width, int length, RoomType rt):this()
public Room(Vector2Int position, int width, int length, RoomType rt) : this()
{
Position = position;
Width = width;
@ -18,9 +40,8 @@ namespace _Project.Scripts.LevelGenerating
exits = new List<ExitsInRoomType>();
}
public Room(Vector2 position, int width, int length, RoomType rt, EntranceToRoomType entrance):this()
public Room(Vector2Int position, int width, int length, RoomType rt, EntranceToRoomType entrance) : this()
{
Position = position;
Width = width;
Length = length;
@ -28,14 +49,15 @@ namespace _Project.Scripts.LevelGenerating
this.entrance = entrance;
}
public Room(int width, int length, RoomType rt):this()
public Room(int width, int length, RoomType rt) : this()
{
Width = width;
Length = length;
this.rt = rt;
}
public Room(Vector2 position, int width, int length, RoomType rt, bool isWallable, EntranceToRoomType entrance, List<ExitsInRoomType> exits)
public Room(Vector2Int position, int width, int length, RoomType rt, bool isWallable,
EntranceToRoomType entrance, List<ExitsInRoomType> exits)
{
Position = position;
Width = width;
@ -46,14 +68,14 @@ namespace _Project.Scripts.LevelGenerating
this.exits = exits;
}
public int Width { get; set; }//width - по оси z
public int Length { get; set; }//length- по оси x
public Vector2 Position;
public int Width { get; set; } // по оси Z, в модулях
public int Length { get; set; } // по оси X, в модулях
public Vector2Int Position; // левый нижний угол, в модулях
public RoomType rt { get; set; }
public EntranceToRoomType entrance { get; set; }
public bool isWallable { get; set; }
public bool isWallable { get; set; } // по факту: комнату ужали об границу карты (стоит переименовать)
public List<ExitsInRoomType> exits { get; set; }
public Vector2 Center => new Vector2(Position.x + Length / 2f, Position.y + Width / 2f);
}
}

View File

@ -5,8 +5,18 @@ using Random = UnityEngine.Random;
namespace _Project.Scripts.LevelGenerating
{
// =====================================================================
// НЕ ЗАБЫТЬ:
// 1. Поля инспектора - в МЕТРАХ. Внутри генератора всё в МОДУЛЯХ (0.5 м), см. ReadSettings().
// 2. Размеры комнат только чётные в модулях (целые метры): метод Even().
// 3. Данные этажа лежат в FloorData (_data). Статический LevelManager.Rooms больше не используется.
// 4. TODO: переименовать Room.isWallable (сейчас значит "комнату ужали об край карты").
// 5. TODO: спавн маркеров в Generate() временный. Он переедет в стадию "оболочка" (стены, пол).
// 6. Этаж с боссом (крыша) сюда не попадает: это FloorKind.Special, собирается вручную.
// =====================================================================
public class WallsGenerator : MonoBehaviour
{
// ---- Настройки в метрах ----
[SerializeField] private int floorsCount;
[SerializeField] private int lengthWallMin;
[SerializeField] private int lengthWallMax;
@ -16,21 +26,30 @@ namespace _Project.Scripts.LevelGenerating
[SerializeField] private int lengthFloor;
[SerializeField] private int corridorLength;
[SerializeField] private int corridorWigth;
[SerializeField] private int minRoomCount;
[SerializeField] private int _minDepthLiftRoom;
[SerializeField] private float _minDistanceStartLift; // метры
[SerializeField] private GameObject GameObjectA;
[SerializeField] private GameObject GameObjectB;
[Header("Генерация")] [SerializeField] private int maxRooms = 15;
[Header("Генерация")]
[SerializeField] private int maxRooms = 15;
[SerializeField, Range(0f, 1f)] private float branchChance = 0.7f;
[SerializeField] private float roomMargin = 2f; // зазор между комнатами
[SerializeField] private float roomMargin = 2f; // зазор между комнатами, метры
[SerializeField] private bool liftDoorAnyPosition = false; // true, если дверь лифта может быть не по центру
// ---- Те же настройки в модулях (заполняет ReadSettings) ----
private int _wallMin, _wallMax, _startSize, _liftLen, _liftWid, _floor, _corrLen, _corrWid, _margin;
private float _minDist;
// ---- Состояние генерации ----
private FloorData _data;
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;
public FloorData Data => _data;
private static readonly ExitsInRoomType[] AllDirs =
{
@ -38,76 +57,174 @@ namespace _Project.Scripts.LevelGenerating
ExitsInRoomType.ZAxisP, ExitsInRoomType.ZAxisN
};
private static readonly RoomType[] ClippedRoles = { RoomType.Laboratory, RoomType.Office, RoomType.Penthouse };
private static readonly RoomType[] FullRoles = { RoomType.Storage, RoomType.SwitchboardRoom, RoomType.BoilerRoom };
void Start()
{
levelManager = LevelManager.Instance;
GenerateFloor();
ReadSettings();
Generate(0); // позже этим будет управлять оркестратор
}
void ReadSettings()
{
_wallMin = LevelGrid.FromMeters(lengthWallMin);
_wallMax = LevelGrid.FromMeters(lengthWallMax);
_startSize = LevelGrid.FromMeters(lengthStartRoom);
_liftLen = LevelGrid.FromMeters(lengthLiftRoom);
_liftWid = LevelGrid.FromMeters(widthLiftRoom);
_floor = LevelGrid.FromMeters(lengthFloor);
_corrLen = LevelGrid.FromMeters(corridorLength);
_corrWid = LevelGrid.FromMeters(corridorWigth);
_margin = Mathf.RoundToInt(roomMargin * LevelGrid.PerMeter);
_minDist = _minDistanceStartLift * LevelGrid.PerMeter;
}
void OnValidate()
{
lengthWallMax = Mathf.Max(lengthWallMax, lengthWallMin);
minRoomCount = Mathf.Min(minRoomCount, maxRooms + 1); // +1 за лифт
if (lengthFloor < lengthStartRoom + corridorLength + Mathf.Max(lengthLiftRoom, widthLiftRoom))
Debug.LogWarning("lengthFloor слишком мал для старта + коридора + лифта");
}
// ---------- Генерация этажа ----------
void ResetState(int floorIndex)
{
_data = FloorData.CreateGenerated(floorIndex, _floor);
_parent.Clear();
_corridorOf.Clear();
_curRooms = 0;
}
public FloorData Generate(int floorIndex)
{
bool ok = false;
// Фаза 1: все ограничения, без обрезки веток
for (int i = 0; i < 50 && !ok; i++)
{
ResetState(floorIndex);
CreateStartRoom();
ok = TryAttachLift(true, _margin, _minDist) && _curRooms >= minRoomCount;
}
// Фаза 2 (аварийная): лифт обязателен, остальное ослабляется
for (int i = 0; i < 20 && !ok; i++)
{
ResetState(floorIndex);
CreateStartRoom();
ok = PlaceElevator();
if (ok) Debug.LogWarning("Этаж собран в аварийном режиме: ограничения ослаблены");
}
if (!ok) Debug.LogError("Лифт не помещается: проверь размеры карты, лифта и коридора");
AssignRoles();
SpawnMarkers(); // временно
return _data;
}
// Роли для комнат без назначения: ужатые об край карты и обычные берут разные пулы
void AssignRoles()
{
foreach (var r in _data.UnassignedRooms.ToList())
{
var pool = r.isWallable ? ClippedRoles : FullRoles;
r.rt = pool[Random.Range(0, pool.Length)];
}
}
// Временная визуализация, переедет в стадию "оболочка"
void SpawnMarkers()
{
Instantiate(GameObjectA, new Vector3(0, -1, 0), Quaternion.identity);
foreach (var room in _data.Rooms)
{
var p = room.Position;
Instantiate(GameObjectB, LevelGrid.ToWorld(new Vector2Int(p.x, p.y)), Quaternion.identity);
Instantiate(GameObjectB, LevelGrid.ToWorld(new Vector2Int(p.x + room.Length, p.y)), Quaternion.identity);
Instantiate(GameObjectB, LevelGrid.ToWorld(new Vector2Int(p.x + room.Length, p.y + room.Width)), Quaternion.identity);
Instantiate(GameObjectB, LevelGrid.ToWorld(new Vector2Int(p.x, p.y + room.Width)), Quaternion.identity);
}
}
// ---------- Лифт ----------
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; // освобождаем место и повторяем
if (TryAttachLift(true, _margin, _minDist)) return true;
if (TryAttachLift(false, _margin, _minDist)) return true;
if (TryAttachLift(false, _margin, 0f)) return true;
if (TryAttachLift(false, 0, 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 };
// Вход в лифт только в длинную стену
private ExitsInRoomType[] LiftEntryDirs()
{
if (_liftLen > _liftWid) return new[] { ExitsInRoomType.ZAxisP, ExitsInRoomType.ZAxisN };
if (_liftWid > _liftLen) return new[] { ExitsInRoomType.XAxisP, ExitsInRoomType.XAxisN };
return AllDirs;
}
}
// Сдвиги лифта вдоль стены, чтобы коридор всё равно попал в стену
private IEnumerable<int> LiftShifts(ExitsInRoomType dir)
{
// Сдвиги лифта вдоль стены (в модулях), если дверь в префабе не обязательно по центру
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;
int cross = xDir ? _liftWid : _liftLen;
int max = (cross - _corrWid) / 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);
bool TryAttachLift(bool requireMinDepth, int margin, float minDistance)
{
var start = _data.Start;
if (start == null) return false;
var parents = _depth.Keys
.OrderByDescending(r => _depth[r])
.ThenByDescending(r => (r.Position - start.Position).sqrMagnitude)
var parents = _data.Depth.Keys
.OrderByDescending(r => _data.Depth[r])
.ThenByDescending(r => (r.Center - start.Center).sqrMagnitude)
.ToList();
foreach (var parent in parents)
{
if (requireMinDepth && _depth[parent] + 1 < _minDepthLiftRoom) continue;
int liftDepth = _data.Depth[parent] + 1;
if (requireMinDepth && liftDepth < _minDepthLiftRoom) continue;
foreach (var dir in LiftEntryDirs().OrderBy(_ => Random.value))
{
if (parent.exits.Contains(dir)) continue;
bool xDir = dir == ExitsInRoomType.XAxisP || dir == ExitsInRoomType.XAxisN;
foreach (int shift in LiftShifts(dir))
{
Room lift = new Room { Length = lengthLiftRoom, Width = widthLiftRoom };
Room lift = new Room { Length = _liftLen, Width = _liftWid };
PlaceByEntry(EntryPoint(parent, dir), dir, lift);
lift.Position += xDir ? new Vector2Int(0, shift) : new Vector2Int(shift, 0);
bool xDir = dir == ExitsInRoomType.XAxisP || dir == ExitsInRoomType.XAxisN;
lift.Position += xDir ? new Vector2(0, shift) : new Vector2(shift, 0);
if (minDistance > 0f && Vector2.Distance(lift.Center, start.Center) < minDistance)
continue;
if (!CanPlaceLift(parent, lift, dir, margin)) continue;
lift.rt = RoomType.Lift;
lift.rt = RoomType.Lift; // тип нужно выставить ДО AddRoom, чтобы FloorData запомнил Lift
lift.exits = new List<ExitsInRoomType>();
lift.entrance = ToEntrance(dir);
lift.isWallable = false;
levelManager.AddRoom(lift);
_depth[lift] = _depth[parent] + 1;
_data.AddRoom(lift, liftDepth);
_parent[lift] = (parent, dir);
_corridorOf[lift] = CreateCorridor(parent, dir);
parent.exits.Add(dir);
@ -117,22 +234,19 @@ bool TryAttachLift(bool requireMinDepth, float margin)
}
}
return false;
}
}
bool CanPlaceLift(Room parent, Room lift, ExitsInRoomType dir, float margin)
{
bool CanPlaceLift(Room parent, Room lift, ExitsInRoomType dir, int 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);
}
return IsAreaFree(Inflate(RoomRect(lift), Mathf.Min(margin, _corrLen)), parent);
}
// Удаляет случайную тупиковую комнату вместе с её коридором
bool PruneRandomLeaf()
{
// Удаляет случайную тупиковую комнату вместе с её коридором
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;
@ -140,74 +254,38 @@ bool PruneRandomLeaf()
Room leaf = leaves[Random.Range(0, leaves.Count)];
var (p, dir) = _parent[leaf];
LevelManager.Rooms.Remove(leaf);
_data.Remove(leaf);
if (_corridorOf.TryGetValue(leaf, out var cor))
{
LevelManager.Rooms.Remove(cor);
_data.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)
{
// ---------- Геометрия (всё в модулях) ----------
private static void PlaceByEntry(Vector2Int 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;
case ExitsInRoomType.XAxisP: n.Position = new Vector2Int(e.x, e.y - n.Width / 2); break;
case ExitsInRoomType.XAxisN: n.Position = new Vector2Int(e.x - n.Length, e.y - n.Width / 2); break;
case ExitsInRoomType.ZAxisP: n.Position = new Vector2Int(e.x - n.Length / 2, e.y); break;
default: n.Position = new Vector2Int(e.x - n.Length / 2, 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;
r.Position.x + r.Length <= _floor &&
r.Position.y + r.Width <= _floor;
// Комнаты проверяются с зазором, коридоры без него (иначе зазор заденет собственный коридор лифта)
void GenerateFloor()
{
const int maxAttempts = 50;
bool ok = false;
for (int i = 0; i < maxAttempts && !ok; i++)
{
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())
{
float x = room.Position.x;
float z = room.Position.y; // Vector2.y -> Z в 3D
float length = room.Length;
float width = room.Width;
Instantiate(GameObjectB, new Vector3(x, 0, z), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(x + length, 0, z), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(x + length, 0, z + width), Quaternion.identity);
Instantiate(GameObjectB, new Vector3(x, 0, z + width), Quaternion.identity);
}
}
// ---------- Вспомогательные ----------
// Чётное значение (целые метры), чтобы центровка (a - b) / 2 была целой
private static int Even(int v) => v & ~1;
private static ExitsInRoomType Opposite(ExitsInRoomType d)
{
@ -231,55 +309,58 @@ private static void PlaceByEntry(Vector2 e, ExitsInRoomType dir, Room n)
}
}
private static Rect RoomRect(Room r) => new Rect(r.Position.x, r.Position.y, r.Length, r.Width);
private static RectInt RoomRect(Room r) => new RectInt(r.Position.x, r.Position.y, r.Length, r.Width);
private static RectInt Inflate(RectInt r, int m) =>
new RectInt(r.x - m, r.y - m, r.width + 2 * m, r.height + 2 * m);
// Прямоугольник коридора, ведущего из prev в направлении dir
private Rect CorridorRect(Room prev, ExitsInRoomType dir)
private RectInt CorridorRect(Room prev, ExitsInRoomType dir)
{
switch (dir)
{
case ExitsInRoomType.XAxisP:
return new Rect(prev.Position.x + prev.Length,
prev.Position.y + (prev.Width - corridorWigth) / 2f,
corridorLength, corridorWigth);
return new RectInt(prev.Position.x + prev.Length,
prev.Position.y + (prev.Width - _corrWid) / 2,
_corrLen, _corrWid);
case ExitsInRoomType.XAxisN:
return new Rect(prev.Position.x - corridorLength,
prev.Position.y + (prev.Width - corridorWigth) / 2f,
corridorLength, corridorWigth);
return new RectInt(prev.Position.x - _corrLen,
prev.Position.y + (prev.Width - _corrWid) / 2,
_corrLen, _corrWid);
case ExitsInRoomType.ZAxisP:
return new Rect(prev.Position.x + (prev.Length - corridorWigth) / 2f,
return new RectInt(prev.Position.x + (prev.Length - _corrWid) / 2,
prev.Position.y + prev.Width,
corridorWigth, corridorLength);
_corrWid, _corrLen);
default: // ZAxisN
return new Rect(prev.Position.x + (prev.Length - corridorWigth) / 2f,
prev.Position.y - corridorLength,
corridorWigth, corridorLength);
return new RectInt(prev.Position.x + (prev.Length - _corrWid) / 2,
prev.Position.y - _corrLen,
_corrWid, _corrLen);
}
}
// Центр стены новой комнаты, куда упирается коридор
private Vector2 EntryPoint(Room prev, ExitsInRoomType dir)
private Vector2Int EntryPoint(Room prev, ExitsInRoomType dir)
{
switch (dir)
{
case ExitsInRoomType.XAxisP:
return new Vector2(prev.Position.x + prev.Length + corridorLength,
prev.Position.y + prev.Width / 2f);
return new Vector2Int(prev.Position.x + prev.Length + _corrLen,
prev.Position.y + prev.Width / 2);
case ExitsInRoomType.XAxisN:
return new Vector2(prev.Position.x - corridorLength,
prev.Position.y + prev.Width / 2f);
return new Vector2Int(prev.Position.x - _corrLen,
prev.Position.y + prev.Width / 2);
case ExitsInRoomType.ZAxisP:
return new Vector2(prev.Position.x + prev.Length / 2f,
prev.Position.y + prev.Width + corridorLength);
return new Vector2Int(prev.Position.x + prev.Length / 2,
prev.Position.y + prev.Width + _corrLen);
default:
return new Vector2(prev.Position.x + prev.Length / 2f,
prev.Position.y - corridorLength);
return new Vector2Int(prev.Position.x + prev.Length / 2,
prev.Position.y - _corrLen);
}
}
private bool IsAreaFree(Rect rect, Room ignore = null)
private bool IsAreaFree(RectInt rect, Room ignore = null)
{
foreach (var other in levelManager.GetRooms())
foreach (var other in _data.Rooms)
{
if (other == ignore) continue;
if (rect.Overlaps(RoomRect(other))) return false;
@ -291,20 +372,25 @@ private static void PlaceByEntry(Vector2 e, ExitsInRoomType dir, Room n)
Room CreateCorridor(Room prev, ExitsInRoomType dir)
{
Rect r = CorridorRect(prev, dir);
RectInt r = CorridorRect(prev, dir);
Room corridor = new Room(r.position,
Mathf.RoundToInt(r.width), Mathf.RoundToInt(r.height),
// Конструктор Room принимает (width = размер по Z, length = размер по X).
// RectInt: width = размер по X, height = размер по Z - поэтому аргументы переставлены.
Room corridor = new Room(r.position, r.height, r.width,
RoomType.Corridor, false,
ToEntrance(dir), new List<ExitsInRoomType> { dir });
LevelManager.Rooms.Add(corridor);
_data.AddCorridor(corridor);
return corridor;
}
// ---------- Размещение комнаты ----------
// Задаёт размер и позицию комнаты от точки входа: вдоль оси выхода комната растёт на полную
// длину, поперёк - симметрично (центр на коридоре). Если не влезает, ужимает.
private bool FitRoom(Room prev, Room n, ExitsInRoomType dir, out bool shrunk)
{
Vector2 e = EntryPoint(prev, dir);
Vector2Int e = EntryPoint(prev, dir);
int origLen = n.Length, origWid = n.Width;
switch (dir)
@ -312,28 +398,28 @@ private static void PlaceByEntry(Vector2 e, ExitsInRoomType dir, Room n)
case ExitsInRoomType.XAxisP:
case ExitsInRoomType.XAxisN:
{
float maxAlong = dir == ExitsInRoomType.XAxisP ? lengthFloor - e.x : e.x;
float maxCross = 2f * Mathf.Min(e.y, lengthFloor - e.y);
n.Length = Mathf.Min(n.Length, Mathf.FloorToInt(maxAlong));
n.Width = Mathf.Min(n.Width, Mathf.FloorToInt(maxCross));
float x = dir == ExitsInRoomType.XAxisP ? e.x : e.x - n.Length;
n.Position = new Vector2(x, e.y - n.Width / 2f);
int maxAlong = dir == ExitsInRoomType.XAxisP ? _floor - e.x : e.x;
int maxCross = 2 * Mathf.Min(e.y, _floor - e.y);
n.Length = Even(Mathf.Min(n.Length, maxAlong));
n.Width = Even(Mathf.Min(n.Width, maxCross));
int x = dir == ExitsInRoomType.XAxisP ? e.x : e.x - n.Length;
n.Position = new Vector2Int(x, e.y - n.Width / 2);
break;
}
default: // Z
{
float maxAlong = dir == ExitsInRoomType.ZAxisP ? lengthFloor - e.y : e.y;
float maxCross = 2f * Mathf.Min(e.x, lengthFloor - e.x);
n.Width = Mathf.Min(n.Width, Mathf.FloorToInt(maxAlong));
n.Length = Mathf.Min(n.Length, Mathf.FloorToInt(maxCross));
float y = dir == ExitsInRoomType.ZAxisP ? e.y : e.y - n.Width;
n.Position = new Vector2(e.x - n.Length / 2f, y);
int maxAlong = dir == ExitsInRoomType.ZAxisP ? _floor - e.y : e.y;
int maxCross = 2 * Mathf.Min(e.x, _floor - e.x);
n.Width = Even(Mathf.Min(n.Width, maxAlong));
n.Length = Even(Mathf.Min(n.Length, maxCross));
int y = dir == ExitsInRoomType.ZAxisP ? e.y : e.y - n.Width;
n.Position = new Vector2Int(e.x - n.Length / 2, y);
break;
}
}
shrunk = n.Length != origLen || n.Width != origWid;
return n.Length >= lengthWallMin && n.Width >= lengthWallMin;
return n.Length >= _wallMin && n.Width >= _wallMin;
}
bool TryPlaceRoom(Room prevRoom, ExitsInRoomType dir)
@ -349,19 +435,14 @@ private static void PlaceByEntry(Vector2 e, ExitsInRoomType dir, Room n)
newRoom.isWallable = shrunk;
// Пересечения: комната (с зазором, без prevRoom) и коридор к ней
Rect roomArea = RoomRect(newRoom);
float m = Mathf.Min(roomMargin, corridorLength);
roomArea.xMin -= m; roomArea.yMin -= m;
roomArea.xMax += m; roomArea.yMax += m;
RectInt roomArea = Inflate(RoomRect(newRoom), Mathf.Min(_margin, _corrLen));
if (!IsAreaFree(roomArea, prevRoom)) return false;
if (!IsAreaFree(CorridorRect(prevRoom, dir))) return false;
// Всё хорошо - фиксируем
levelManager.AddRoom(newRoom);
_depth[newRoom] = _depth[prevRoom] + 1;
_data.AddRoom(newRoom, _data.Depth[prevRoom] + 1);
_parent[newRoom] = (prevRoom, dir);
_corridorOf[newRoom] = CreateCorridor(prevRoom, dir); // вместо отдельного CreateCorridor(...)
_corridorOf[newRoom] = CreateCorridor(prevRoom, dir);
_curRooms++;
if (!prevRoom.exits.Contains(dir)) prevRoom.exits.Add(dir);
@ -377,13 +458,10 @@ private static void PlaceByEntry(Vector2 e, ExitsInRoomType dir, Room n)
Room GenerateRandomRoom()
{
// Range(int,int) не включает верхнюю границу, поэтому +1
// Размер - целые метры (в модулях всегда чётный). Range(int,int) не включает верхнюю границу, поэтому +1
int a = Random.Range(lengthWallMin, lengthWallMax + 1);
int b = Random.Range(lengthWallMin, lengthWallMax + 1);
Room room = new Room();
room.Length = a;
room.Width = b;
return room;
return new Room { Length = LevelGrid.FromMeters(a), Width = LevelGrid.FromMeters(b) };
}
// ---------- Стартовая комната ----------
@ -394,70 +472,69 @@ private static void PlaceByEntry(Vector2 e, ExitsInRoomType dir, Room n)
int xR, zR;
ExitsInRoomType firstDir;
Room room = new Room(lengthStartRoom, lengthStartRoom, RoomType.Start);
Room room = new Room(_startSize, _startSize, RoomType.Start);
room.exits = new List<ExitsInRoomType>();
room.entrance = EntranceToRoomType.Null;
if (randValue < 0.25f) // 1 квадрант
{
xR = Random.Range(0, lengthFloor / 2 - lengthStartRoom);
if (xR + lengthStartRoom <= lengthFloor / 4)
xR = Random.Range(0, _floor / 2 - _startSize);
if (xR + _startSize <= _floor / 4)
{
firstDir = ExitsInRoomType.XAxisP;
zR = Random.Range(0, lengthFloor / 2 - lengthStartRoom);
zR = Random.Range(0, _floor / 2 - _startSize);
}
else
{
firstDir = ExitsInRoomType.ZAxisP;
zR = Random.Range(0, lengthFloor / 4 - lengthStartRoom);
zR = Random.Range(0, _floor / 4 - _startSize);
}
}
else if (randValue < 0.5f) // 2 квадрант
{
xR = Random.Range(lengthFloor / 2, lengthFloor - lengthStartRoom);
if (xR + lengthStartRoom <= lengthFloor * 3 / 4)
xR = Random.Range(_floor / 2, _floor - _startSize);
if (xR + _startSize <= _floor * 3 / 4)
{
firstDir = ExitsInRoomType.ZAxisP;
zR = Random.Range(0, lengthFloor / 4 - lengthStartRoom);
zR = Random.Range(0, _floor / 4 - _startSize);
}
else
{
firstDir = ExitsInRoomType.XAxisN;
zR = Random.Range(0, lengthFloor / 2 - lengthStartRoom);
zR = Random.Range(0, _floor / 2 - _startSize);
}
}
else if (randValue < 0.75f) // 3 квадрант
{
xR = Random.Range(lengthFloor / 2, lengthFloor - lengthStartRoom);
if (xR + lengthStartRoom <= lengthFloor * 3 / 4)
xR = Random.Range(_floor / 2, _floor - _startSize);
if (xR + _startSize <= _floor * 3 / 4)
{
firstDir = ExitsInRoomType.ZAxisN;
zR = Random.Range(lengthFloor * 3 / 4, lengthFloor - lengthStartRoom);
zR = Random.Range(_floor * 3 / 4, _floor - _startSize);
}
else
{
firstDir = ExitsInRoomType.XAxisN;
zR = Random.Range(lengthFloor / 2, lengthFloor - lengthStartRoom);
zR = Random.Range(_floor / 2, _floor - _startSize);
}
}
else // 4 квадрант
{
xR = Random.Range(0, lengthFloor / 2 - lengthStartRoom);
if (xR + lengthStartRoom <= lengthFloor / 4)
xR = Random.Range(0, _floor / 2 - _startSize);
if (xR + _startSize <= _floor / 4)
{
firstDir = ExitsInRoomType.XAxisP;
zR = Random.Range(lengthFloor / 2, lengthFloor - lengthStartRoom);
zR = Random.Range(_floor / 2, _floor - _startSize);
}
else
{
firstDir = ExitsInRoomType.ZAxisN;
zR = Random.Range(lengthFloor * 3 / 4, lengthFloor - lengthStartRoom);
zR = Random.Range(_floor * 3 / 4, _floor - _startSize);
}
}
room.Position = new Vector2(xR, zR);
LevelManager.Rooms.Add(room);
_depth[room] = 0;
room.Position = new Vector2Int(xR, zR);
_data.AddRoom(room, 0); // rt = Start, поэтому FloorData запомнит _data.Start
_curRooms = 1; // стартовая комната считается
// Предпочтительное направление, а при неудаче - остальные
@ -470,32 +547,26 @@ private static void PlaceByEntry(Vector2 e, ExitsInRoomType dir, Room n)
void OnDrawGizmos()
{
var rooms = LevelManager.Rooms;
if (rooms == null) return;
if (_data == null) return;
foreach (var r in rooms)
foreach (var r in _data.Rooms)
{
switch (r.rt)
{
case RoomType.Start:
Gizmos.color = Color.green;
break;
case RoomType.Corridor:
Gizmos.color = Color.yellow;
break;
case RoomType.Lift:
Gizmos.color = Color.rebeccaPurple;
break;
default:
Gizmos.color = Color.cyan;
break;
case RoomType.Start: Gizmos.color = Color.green; break;
case RoomType.Corridor: Gizmos.color = Color.yellow; break;
case RoomType.Lift: Gizmos.color = Color.rebeccaPurple; break;
case RoomType.Laboratory: Gizmos.color = Color.white; break;
case RoomType.Office: Gizmos.color = Color.blue; break;
case RoomType.Penthouse: Gizmos.color = Color.magenta; break;
case RoomType.Storage: Gizmos.color = new Color(1f, 0.5f, 0f); break;
case RoomType.SwitchboardRoom: Gizmos.color = Color.red; break;
case RoomType.BoilerRoom: Gizmos.color = Color.gray; break;
default: Gizmos.color = Color.cyan; break;
}
// Gizmos.color = r.rt == RoomType.Start ? Color.green
// : r.rt == RoomType.Corridor ? Color.yellow : Color.cyan;
Vector3 c = new Vector3(r.Position.x + r.Length / 2f, 0, r.Position.y + r.Width / 2f);
Gizmos.DrawWireCube(c, new Vector3(r.Length, 0.1f, r.Width));
Vector3 c = LevelGrid.ToWorld(r.Center);
Gizmos.DrawWireCube(c, new Vector3(r.Length * LevelGrid.Module, 0.1f, r.Width * LevelGrid.Module));
}
}
}

3
Souljack.slnx Normal file
View File

@ -0,0 +1,3 @@
<Solution>
<Project Path="Assembly-CSharp.csproj" />
</Solution>