готовый генератор комнат
This commit is contained in:
parent
9519be076f
commit
650af515e9
6
.vsconfig
Normal file
6
.vsconfig
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"components": [
|
||||
"Microsoft.VisualStudio.Workload.ManagedGame"
|
||||
]
|
||||
}
|
||||
@ -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:
|
||||
|
||||
139
Assets/_Project/Materials/New Material 2.mat
Normal file
139
Assets/_Project/Materials/New Material 2.mat
Normal 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
|
||||
8
Assets/_Project/Materials/New Material 2.mat.meta
Normal file
8
Assets/_Project/Materials/New Material 2.mat.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4555641771ab6d4c81ed310ee2dc560
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
146
Assets/_Project/Prefabs/GameObjectC.prefab
Normal file
146
Assets/_Project/Prefabs/GameObjectC.prefab
Normal 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}
|
||||
7
Assets/_Project/Prefabs/GameObjectC.prefab.meta
Normal file
7
Assets/_Project/Prefabs/GameObjectC.prefab.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 222a3fec38ee4e94da1bdcccb5c71aae
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
89
Assets/_Project/Scripts/LevelGenerating/FloorData.cs
Normal file
89
Assets/_Project/Scripts/LevelGenerating/FloorData.cs
Normal 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 };
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c44c2a7978314fbeb585ae58fa82d95d
|
||||
timeCreated: 1791498732
|
||||
@ -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;
|
||||
@ -35,7 +56,8 @@ namespace _Project.Scripts.LevelGenerating
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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 };
|
||||
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)
|
||||
{
|
||||
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)
|
||||
bool TryAttachLift(bool requireMinDepth, int margin, float minDistance)
|
||||
{
|
||||
var start = levelManager.GetRooms().First(r => r.rt == RoomType.Start);
|
||||
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);
|
||||
@ -119,15 +236,12 @@ 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);
|
||||
}
|
||||
|
||||
// Удаляет случайную тупиковую комнату вместе с её коридором
|
||||
@ -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
3
Souljack.slnx
Normal file
@ -0,0 +1,3 @@
|
||||
<Solution>
|
||||
<Project Path="Assembly-CSharp.csproj" />
|
||||
</Solution>
|
||||
Loading…
Reference in New Issue
Block a user