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