diff --git a/aplib.net-demo/Assets/Scenes/ConnectedComponents.unity b/aplib.net-demo/Assets/Scenes/ConnectedComponents.unity new file mode 100644 index 000000000..854c2cec5 --- /dev/null +++ b/aplib.net-demo/Assets/Scenes/ConnectedComponents.unity @@ -0,0 +1,432 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.18029143, g: 0.22572419, b: 0.30693057, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &24601448 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 24601450} + - component: {fileID: 24601449} + - component: {fileID: 24601451} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &24601449 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 24601448} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &24601450 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 24601448} + serializedVersion: 2 + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!114 &24601451 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 24601448} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Version: 3 + m_UsePipelineSettings: 1 + m_AdditionalLightsShadowResolutionTier: 2 + m_LightLayerMask: 1 + m_RenderingLayers: 1 + m_CustomShadowLayers: 0 + m_ShadowLayerMask: 1 + m_ShadowRenderingLayers: 1 + m_LightCookieSize: {x: 1, y: 1} + m_LightCookieOffset: {x: 0, y: 0} + m_SoftShadowQuality: 0 +--- !u!1 &1378037378 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1378037381} + - component: {fileID: 1378037380} + - component: {fileID: 1378037379} + - component: {fileID: 1378037382} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1378037379 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1378037378} + m_Enabled: 1 +--- !u!20 &1378037380 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1378037378} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1378037381 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1378037378} + serializedVersion: 2 + m_LocalRotation: {x: 0.428509, y: -0, z: -0, w: 0.9035376} + m_LocalPosition: {x: 34, y: 67.6, z: -23.4} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 50.746, y: 0, z: 0} +--- !u!114 &1378037382 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1378037378} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_VolumeFrameworkUpdateModeOption: 2 + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_AllowHDROutput: 1 + m_UseScreenCoordOverride: 0 + m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0} + m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0} + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 + m_TaaSettings: + quality: 3 + frameInfluence: 0.1 + jitterScale: 1 + mipBias: 0 + varianceClampScale: 0.9 + contrastAdaptiveSharpening: 0 +--- !u!1 &1455078761 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1455078763} + - component: {fileID: 1455078762} + m_Layer: 0 + m_Name: Grid + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1455078762 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1455078761} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bf81f8d2562f471f8fe5ad818d5670e6, type: 3} + m_Name: + m_EditorClassIdentifier: + RoomObjects: {fileID: 11400000, guid: 9962865d7cc12a74abe02efdb8b7d072, type: 2} +--- !u!4 &1455078763 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1455078761} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 1378037381} + - {fileID: 24601450} + - {fileID: 1455078763} diff --git a/aplib.net-demo/Assets/Scenes/ConnectedComponents.unity.meta b/aplib.net-demo/Assets/Scenes/ConnectedComponents.unity.meta new file mode 100644 index 000000000..2564f124d --- /dev/null +++ b/aplib.net-demo/Assets/Scenes/ConnectedComponents.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d2f440c864cdffb6a93277b407cdc730 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/aplib.net-demo/Assets/Scripts/Tiles/Corner.cs b/aplib.net-demo/Assets/Scripts/Tiles/Corner.cs index 1ad77894f..d79de9de4 100644 --- a/aplib.net-demo/Assets/Scripts/Tiles/Corner.cs +++ b/aplib.net-demo/Assets/Scripts/Tiles/Corner.cs @@ -5,15 +5,15 @@ namespace Assets.Scripts.Tiles /// /// Represents a corner tile. /// ___ ___ - /// | | |_| - /// | |____ + /// |_| | | + /// ____| | /// |_____| /// public class Corner : Tile { /// /// Initializes a new instance of the class. - /// The default is a top-right corner. + /// The default is a top-left corner. /// /// The amount of times to rotate the tile. public Corner(int rotate = 0) @@ -21,7 +21,7 @@ public Corner(int rotate = 0) Rotation = rotate; AllowedDirections = new List { false, false, false, false }; - int index = rotate % 4; + int index = (rotate + 3) % 4; int nextIndex = (index + 1) % 4; AllowedDirections[index] = true; diff --git a/aplib.net-demo/Assets/Scripts/Tiles/Straight.cs b/aplib.net-demo/Assets/Scripts/Tiles/Straight.cs index 43c25380d..a65eb1699 100644 --- a/aplib.net-demo/Assets/Scripts/Tiles/Straight.cs +++ b/aplib.net-demo/Assets/Scripts/Tiles/Straight.cs @@ -18,6 +18,7 @@ public class Straight : Tile /// The amount of times to rotate the tile. public Straight(int rotate = 0) { + rotate %= 4; Rotation = rotate; bool isVertical = rotate % 2 == 0; diff --git a/aplib.net-demo/Assets/Scripts/Tiles/TSection.cs b/aplib.net-demo/Assets/Scripts/Tiles/TSection.cs index 716091df3..ea68e08a4 100644 --- a/aplib.net-demo/Assets/Scripts/Tiles/TSection.cs +++ b/aplib.net-demo/Assets/Scripts/Tiles/TSection.cs @@ -4,16 +4,16 @@ namespace Assets.Scripts.Tiles { /// /// Represents a T-section tile. - /// _______ - /// |_____| /// ___ ___ /// |_| |_| + /// _______ + /// |_____| /// public class TSection : Tile { /// /// Initializes a new instance of the class. - /// The default is a T-section with the top side closed. + /// The default is a T-section with the top side opened. /// /// The amount of times to rotate the tile. public TSection(int rotate = 0) @@ -21,7 +21,7 @@ public TSection(int rotate = 0) Rotation = rotate; AllowedDirections = new List { true, true, true, true }; - int index = rotate % 4; + int index = (rotate + 2) % 4; AllowedDirections[index] = false; } diff --git a/aplib.net-demo/Assets/Scripts/Tiles/Tile.cs b/aplib.net-demo/Assets/Scripts/Tiles/Tile.cs index faa76acc3..1477c7507 100644 --- a/aplib.net-demo/Assets/Scripts/Tiles/Tile.cs +++ b/aplib.net-demo/Assets/Scripts/Tiles/Tile.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using UnityEngine; namespace Assets.Scripts.Tiles { @@ -7,6 +8,8 @@ namespace Assets.Scripts.Tiles /// public abstract class Tile { + public GameObject GameObject { get; set; } + /// /// The rotation of the tile. 0 = 0 degrees, 1 = 90 degrees, 2 = 180 degrees, 3 = 270 degrees. /// diff --git a/aplib.net-demo/Assets/Scripts/WFC/Cell.cs b/aplib.net-demo/Assets/Scripts/WFC/Cell.cs index 15b3552ae..4518a7213 100644 --- a/aplib.net-demo/Assets/Scripts/WFC/Cell.cs +++ b/aplib.net-demo/Assets/Scripts/WFC/Cell.cs @@ -18,11 +18,18 @@ public class Cell /// public List Candidates { get; set; } + public int X { get; } + + public int Y { get; } + /// /// Initializes a new instance of the class. - /// - public Cell() + /// // TODO comment posX/Y on both constructors + public Cell(int posX, int posY) { + X = posX; + Y = posY; + Tile = new Empty(); Candidates = new List() { @@ -49,8 +56,11 @@ public Cell() /// Initializes a new instance of the class. /// /// The possible tiles that can be placed in this cell. - public Cell(List tiles) + public Cell(int posX, int posY, List tiles) { + X = posX; + Y = posY; + Tile = new Empty(); Candidates = tiles; } diff --git a/aplib.net-demo/Assets/Scripts/WFC/Grid.cs b/aplib.net-demo/Assets/Scripts/WFC/Grid.cs index 3efbe9608..ab1c44ccc 100644 --- a/aplib.net-demo/Assets/Scripts/WFC/Grid.cs +++ b/aplib.net-demo/Assets/Scripts/WFC/Grid.cs @@ -1,5 +1,8 @@ using Assets.Scripts.Tiles; +using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; namespace Assets.Scripts.WFC { @@ -42,8 +45,8 @@ public Grid(int width, int height) /// The y-coordinate of the cell. public Cell this[int x, int y] { - get => _cells[(y * Width) + x]; - set => _cells[(y * Width) + x] = value; + get => _cells[CoordinatesToIndex(x, y)]; + set => _cells[CoordinatesToIndex(x, y)] = value; } /// @@ -62,7 +65,18 @@ public Cell this[int index] public void Init() { for (int i = 0; i < Width * Height; i++) - _cells.Add(new Cell()); + { + (int x, int y) = IndexToCoordinates(i); + _cells.Add(new Cell(x, y)); + } + } + + protected (int x, int y) IndexToCoordinates(int index) => (index % Width, index / Width); + + protected int CoordinatesToIndex(int x, int y) + { + if (x >= Width || y >= Height) throw new IndexOutOfRangeException("Coordinates specified are out of range."); + return y * Width + x; } /// @@ -76,5 +90,90 @@ public void PlaceRoom(int x, int y, Room room) this[x, y].Tile = room; this[x, y].Candidates = new List(); } + + public ICollection Get4NeighbouringCells(Cell cell) + { + ICollection neighbours = new Collection(); + if (cell.X > 0) neighbours.Add(this[cell.X - 1, cell.Y]); + if (cell.X < Width - 1) neighbours.Add(this[cell.X + 1, cell.Y]); + if (cell.Y > 0) neighbours.Add(this[cell.X, cell.Y - 1]); + if (cell.Y < Height - 1) neighbours.Add(this[cell.X, cell.Y + 1]); + return neighbours; + } + + public ICollection Get8NeighbouringCells(Cell cell) + { + ICollection neighbours = new Collection(); + for (int i = -1; i < 2; i++) + { + if (cell.X + i < 0 || cell.X + i >= Width) continue; // No out of range + for (int j = -1; j < 2; j++) + { + if (i == j && i == 0) continue; // Skip the cell itself + if (cell.Y + j < 0 || cell.Y + j >= Height) continue; // No out of range + neighbours.Add(this[cell.X + i, cell.Y + j]); + } + } + return neighbours; + } + + /// + /// + /// + /// + /// + /// Assumes that the cells are assigned a tile + public ICollection GetConnectedNeighbours(Cell cell) + { + ICollection connectedNeighbours = new Collection(); + ICollection neighbours = Get4NeighbouringCells(cell); // Note: no diagonal neighbours + foreach (Cell neighbour in neighbours) + { + if (cell.Tile.CanConnectInDirection(1) && neighbour.X > cell.X && neighbour.Tile.CanConnectInDirection(3)) + connectedNeighbours.Add(neighbour); + else if (cell.Tile.CanConnectInDirection(3) && neighbour.X < cell.X && neighbour.Tile.CanConnectInDirection(1)) + connectedNeighbours.Add(neighbour); + else if (cell.Tile.CanConnectInDirection(0) && neighbour.Y > cell.Y && neighbour.Tile.CanConnectInDirection(2)) + connectedNeighbours.Add(neighbour); + else if (cell.Tile.CanConnectInDirection(2) && neighbour.Y < cell.Y && neighbour.Tile.CanConnectInDirection(0)) + connectedNeighbours.Add(neighbour); + } + + return connectedNeighbours; + } + + public IList> DetermineConnectedComponents() + { + ISet unvisitedCells = new HashSet(_cells.Where(cell => cell.Tile is not Empty)); // Deep copy + IList> connectedComponents = new List>(); + + while (unvisitedCells.Any()) + { + ISet connectedComponent = new HashSet(); + connectedComponents.Add(connectedComponent); + + // Determine connected component, which updates unvisitedCells and connectedComponent + DetermineSingleConnectedComponent(unvisitedCells, connectedComponent, unvisitedCells.First()); + } + + return connectedComponents; + } + + public void DetermineSingleConnectedComponent(in ISet unvisitedCells, in ISet connectedComponent, Cell cell) + { + connectedComponent.Add(cell); + unvisitedCells.Remove(cell); + + ICollection connectedNeighbours = GetConnectedNeighbours(cell); + foreach (Cell connectedNeighbour in connectedNeighbours) + { + if (!unvisitedCells.Contains(connectedNeighbour)) continue; // Already visited + + connectedComponent.Add(connectedNeighbour); + unvisitedCells.Remove(connectedNeighbour); + + DetermineSingleConnectedComponent(unvisitedCells, connectedComponent, connectedNeighbour); + } + } } } diff --git a/aplib.net-demo/Assets/Scripts/WFC/GridPlacer.cs b/aplib.net-demo/Assets/Scripts/WFC/GridPlacer.cs index 959677105..9eee5301d 100644 --- a/aplib.net-demo/Assets/Scripts/WFC/GridPlacer.cs +++ b/aplib.net-demo/Assets/Scripts/WFC/GridPlacer.cs @@ -1,5 +1,6 @@ using Assets.Scripts.Tiles; using System.Collections.Generic; +using System.Linq; using UnityEngine; namespace Assets.Scripts.WFC @@ -35,7 +36,7 @@ public class GridPlacer : MonoBehaviour public RoomObjects RoomObjects; /// - /// Awake is called when the script instance is being loaded. + /// This contains the whole 'pipeline' of level generation, including initialising the grid and placing teleporters. /// public void Awake() { @@ -52,6 +53,8 @@ public void Awake() PlaceTile(x, y, _grid[x, y].Tile); } } + + JoinConnectedComponentsWithTeleporters(); } /// @@ -59,22 +62,22 @@ public void Awake() /// public void TempFillFunction() { - _grid.PlaceRoom(2, 1, new Room(new List { false, true, true, false })); - - _grid.PlaceRoom(4, 4, new Room(new List { false, true, true, false })); - - // Road 1 - _grid[2, 2].Tile = new Straight(); - _grid[2, 3].Tile = new Straight(); - _grid[2, 4].Tile = new Corner(2); - _grid[3, 4].Tile = new Straight(1); - - // Road 2 - _grid[3, 1].Tile = new Straight(1); - _grid[4, 1].Tile = new TSection(3); - _grid[4, 2].Tile = new Straight(); - _grid[4, 3].Tile = new Straight(); - _grid[4, 0].Tile = new DeadEnd(); + _grid[0, 0].Tile = new TSection(3); + _grid[0, 1].Tile = new Crossing(); + _grid[0, 2].Tile = new DeadEnd(2); + _grid[0, 3].Tile = new Straight(1); + _grid[1, 0].Tile = new TSection(); + _grid[1, 1].Tile = new Straight(); + _grid[1, 2].Tile = new Corner(2); + _grid[1, 3].Tile = new Crossing(); + _grid[2, 0].Tile = new Corner(); + _grid.PlaceRoom(2, 1, new Room(new List { true, true, true, true })); + + _grid.PlaceRoom(3, 3, new Room(new List { true, true, true, true })); + _grid[3, 2].Tile = new Corner(1); + _grid[4, 2].Tile = new TSection(3); + _grid[4, 1].Tile = new Straight(); + _grid.PlaceRoom(4, 0, new Room(new List { true, true, true, true })); } /// @@ -87,18 +90,40 @@ public void PlaceTile(int x, int y, Tile tile) { GameObject prefab = tile switch { - Corner _ => RoomObjects.Corner, - Crossing _ => RoomObjects.Crossing, - DeadEnd _ => RoomObjects.DeadEnd, - Empty _ => RoomObjects.Empty, - Room _ => RoomObjects.Room, - Straight _ => RoomObjects.Straight, - TSection _ => RoomObjects.TSection, - _ => null + Corner => RoomObjects.Corner, + Crossing => RoomObjects.Crossing, + DeadEnd => RoomObjects.DeadEnd, + Empty => RoomObjects.Empty, + Room => RoomObjects.Room, + Straight => RoomObjects.Straight, + TSection => RoomObjects.TSection, + _ => null }; if (prefab != null) - _ = Instantiate(prefab, new Vector3(x * _tileSizeX, 0, y * _tileSizeY), Quaternion.Euler(0, tile.Rotation * _tileRotation, 0), transform); + { + tile.GameObject = Instantiate(prefab, + new Vector3(x * _tileSizeX, 0, y * _tileSizeY), + Quaternion.Euler(0, tile.Rotation * _tileRotation, 0), + transform); + } } + + private void JoinConnectedComponentsWithTeleporters() + { + IList> connectedComponents = _grid.DetermineConnectedComponents(); + + // We draw all the connected components individually + foreach (ISet connectedComponent in connectedComponents) + { + Color color = GetUnusedColor(); + foreach (Cell cell in connectedComponent) + cell.Tile.GameObject.GetComponent().material.color = color; + } + } + + private static Color[] _colors = { Color.red, Color.blue, Color.green, Color.yellow, Color.magenta, Color.cyan }; + private static int _colorIndex = -1; + private static Color GetUnusedColor() => _colors[_colorIndex = (_colorIndex + 1) % _colors.Length]; } }