diff --git a/Assets/Scenes/Dev/WavefunctionCollapseProceduralGeneration.unity b/Assets/Scenes/Dev/WavefunctionCollapseProceduralGeneration.unity index 3bf9e6d7..f4a89645 100644 --- a/Assets/Scenes/Dev/WavefunctionCollapseProceduralGeneration.unity +++ b/Assets/Scenes/Dev/WavefunctionCollapseProceduralGeneration.unity @@ -163,6 +163,13 @@ MonoBehaviour: closedBorders: 1 delayAwait: 1 delayCoroutine: 0.001 + branchFrequency: 0.3 + branchLengthMin: 2 + branchLengthMax: 4 + useCustomStartEnd: 1 + customStartPoint: {x: 1, y: 1} + customEndPoint: {x: 13, y: 13} + enforceMainPath: 1 --- !u!4 &202619012 Transform: m_ObjectHideFlags: 0 @@ -840,27 +847,27 @@ MonoBehaviour: - {fileID: 2704001218989138493, guid: 3162a96ef13239b41ba3d21ca801cdc4, type: 3} - {fileID: 2704001218989138493, guid: d9e49365637f4444a99840103c5d5a44, type: 3} - {fileID: 2704001218989138493, guid: 80ed2fe86d7677d498f807270b37b1f7, type: 3} - - {fileID: 7919088255103129344, guid: a057db1023afbb44993f4161fe103b41, type: 3} - - {fileID: 7919088255103129344, guid: 503e9bc31e02ce046bda52996a26fe22, type: 3} - - {fileID: 7255329216686614878, guid: a809dc39cad8b27429e59a86867fd6d6, type: 3} - - {fileID: 7919088255103129344, guid: a00c0b36ee4586144a9ec800f0d38830, type: 3} - - {fileID: 2704001218989138493, guid: 989f0d6e8bde6834ebbd740844aeba0e, type: 3} - - {fileID: 7919088255103129344, guid: 55a0e845f95f1974f880c446a4af8303, type: 3} - - {fileID: 7919088255103129344, guid: 2653cb9ee3eaab74ca647ba7532e1608, type: 3} - - {fileID: 7919088255103129344, guid: 1ff71b2adbd7a854cb82ddfd95f25afb, type: 3} - - {fileID: 7919088255103129344, guid: 963551cb04192a84088ddaaa3500f559, type: 3} - - {fileID: 7919088255103129344, guid: 9a95e856383ff334b8850ad578cb6d65, type: 3} - - {fileID: 7919088255103129344, guid: 31934c838854a3c4baa7ad791fd520d3, type: 3} - - {fileID: 7919088255103129344, guid: 31dfb5210a9d64b41bb417620dd68533, type: 3} - - {fileID: 2704001218989138493, guid: 2fce0180ccd18534faacb5e22db086b9, type: 3} - - {fileID: 2704001218989138493, guid: 10dc33d8ada12f84ebd7234446f34c45, type: 3} - - {fileID: 7919088255103129344, guid: 09343d8e8bc60e445bae34f6881ffe59, type: 3} - - {fileID: 7919088255103129344, guid: 2b7862a7ca7290d4c922cecdd20528bb, type: 3} - - {fileID: 7919088255103129344, guid: cc3828d7e55dcf04b85290093563dff2, type: 3} - {fileID: 2704001218989138493, guid: d32ae7acb5c9b0f489ce40dc2e15c772, type: 3} - - {fileID: 7919088255103129344, guid: e2e591d216fb2734aba113997efa3797, type: 3} - - {fileID: 7919088255103129344, guid: 2bbd6e86a833c8a4e9ee47a195ec46c1, type: 3} + - {fileID: 7919088255103129344, guid: cc3828d7e55dcf04b85290093563dff2, type: 3} + - {fileID: 7919088255103129344, guid: 2b7862a7ca7290d4c922cecdd20528bb, type: 3} + - {fileID: 7919088255103129344, guid: 31934c838854a3c4baa7ad791fd520d3, type: 3} - {fileID: 7919088255103129344, guid: 1e236b387bdb75c4ab929f783d7e3f24, type: 3} + - {fileID: 7919088255103129344, guid: 2653cb9ee3eaab74ca647ba7532e1608, type: 3} + - {fileID: 7919088255103129344, guid: 31dfb5210a9d64b41bb417620dd68533, type: 3} + - {fileID: 7919088255103129344, guid: 2bbd6e86a833c8a4e9ee47a195ec46c1, type: 3} + - {fileID: 7919088255103129344, guid: e2e591d216fb2734aba113997efa3797, type: 3} + - {fileID: 7919088255103129344, guid: 1ff71b2adbd7a854cb82ddfd95f25afb, type: 3} + - {fileID: 2704001218989138493, guid: 10dc33d8ada12f84ebd7234446f34c45, type: 3} + - {fileID: 7255329216686614878, guid: a809dc39cad8b27429e59a86867fd6d6, type: 3} + - {fileID: 7919088255103129344, guid: 503e9bc31e02ce046bda52996a26fe22, type: 3} + - {fileID: 7919088255103129344, guid: a057db1023afbb44993f4161fe103b41, type: 3} + - {fileID: 7919088255103129344, guid: 963551cb04192a84088ddaaa3500f559, type: 3} + - {fileID: 7919088255103129344, guid: 09343d8e8bc60e445bae34f6881ffe59, type: 3} + - {fileID: 7919088255103129344, guid: 9a95e856383ff334b8850ad578cb6d65, type: 3} + - {fileID: 7919088255103129344, guid: 55a0e845f95f1974f880c446a4af8303, type: 3} + - {fileID: 2704001218989138493, guid: 989f0d6e8bde6834ebbd740844aeba0e, type: 3} + - {fileID: 7919088255103129344, guid: a00c0b36ee4586144a9ec800f0d38830, type: 3} + - {fileID: 2704001218989138493, guid: 2fce0180ccd18534faacb5e22db086b9, type: 3} - {fileID: 7919088255103129344, guid: f818560f421c6f348b9d516c62c52d09, type: 3} --- !u!4 &2078654584 Transform: diff --git a/Assets/Scripts/-ProceduralGeneration/WavefunctionCollapse/Scripts/WFCGenerator.cs b/Assets/Scripts/-ProceduralGeneration/WavefunctionCollapse/Scripts/WFCGenerator.cs index 4e0db641..55d84888 100644 --- a/Assets/Scripts/-ProceduralGeneration/WavefunctionCollapse/Scripts/WFCGenerator.cs +++ b/Assets/Scripts/-ProceduralGeneration/WavefunctionCollapse/Scripts/WFCGenerator.cs @@ -26,9 +26,27 @@ public class WFCGenerator : MonoBehaviour [Space] [SerializeField] private int delayAwait = 3000; [SerializeField] private float delayCoroutine = 3f; + [Space] + [SerializeField] private float branchFrequency = 0.3f; + [SerializeField] private int branchLengthMin = 2; + [SerializeField] private int branchLengthMax = 4; + [Space] + [SerializeField] private bool useCustomStartEnd = false; + [SerializeField] private Vector2Int customStartPoint = new Vector2Int(1, 1); + [SerializeField] private Vector2Int customEndPoint = new Vector2Int(13, 13); + + // Possible spawn points and exit points + private Vector2Int startPoint; + private Vector2Int endPoint; + private List possibleSpawnPoints = new List(); + private List possibleExitPoints = new List(); bool broken = false; + [SerializeField] private bool enforceMainPath = true; + private List mainPath = new List(); + private HashSet guaranteedConnections = new HashSet(); + private void Start() { chunkCompatibilityMatrix.BuildCompatibilityMatrix(); @@ -36,12 +54,230 @@ public class WFCGenerator : MonoBehaviour if (randomizeSeed) seed = UnityEngine.Random.Range(0, 1000000); - UnityEngine.Random.InitState(seed); + + if (enforceMainPath) + { + GenerateMainPath(); + } + + InitializeGrid(chunkCompatibilityMatrix.prefabs); GenerateMap(); } + private void GenerateMainPath() + { + DetermineStartAndEndPoints(); + + // Start from bottom-left + Vector2Int current = startPoint; + mainPath.Add(current); + guaranteedConnections.Add(current); + guaranteedConnections.Add(endPoint); + + while (current != endPoint) + { + Vector2Int next = GetNextPathPoint(current, endPoint); + + if (!IsOutOfBounds(next) && !mainPath.Contains(next)) + { + current = next; + mainPath.Add(current); + guaranteedConnections.Add(current); + + // Add some branching paths + if (UnityEngine.Random.value < branchFrequency) + { + AddBranchPath(current); + } + } + } + } + + private void AddBranchPath(Vector2Int start) + { + Vector2Int current = start; + int branchLength = UnityEngine.Random.Range(branchLengthMin, branchLengthMax); + + for (int i = 0; i < branchLength; i++) + { + Vector2Int next; + if (UnityEngine.Random.value < 0.5f) + next = current + Vector2Int.right * (UnityEngine.Random.value < 0.5f ? 1 : -1); + else + next = current + Vector2Int.up; + + if (!IsOutOfBounds(next) && !mainPath.Contains(next) && + next.x > 0 && next.x < gridWidth - 1 && + next.y > 0 && next.y < gridHeight - 1) + { + current = next; + guaranteedConnections.Add(current); + } + else + break; + } + } + private void DetermineStartAndEndPoints() + { + if (useCustomStartEnd) + { + startPoint = customStartPoint; + endPoint = customEndPoint; + } + else + { + // Generate multiple possible spawn points in the bottom third + for (int x = 1; x < gridWidth - 1; x++) + { + for (int y = 1; y < gridHeight / 3; y++) + { + possibleSpawnPoints.Add(new Vector2Int(x, y)); + } + } + + // Generate multiple possible exit points in the top third + for (int x = 1; x < gridWidth - 1; x++) + { + for (int y = (gridHeight * 2) / 3; y < gridHeight - 1; y++) + { + possibleExitPoints.Add(new Vector2Int(x, y)); + } + } + + // Pick random points from the possible locations + startPoint = possibleSpawnPoints[UnityEngine.Random.Range(0, possibleSpawnPoints.Count)]; + endPoint = possibleExitPoints[UnityEngine.Random.Range(0, possibleExitPoints.Count)]; + } + } + + private bool IsBorderPosition(Vector2Int pos) + { + return pos.x == 0 || pos.x == gridWidth - 1 || + pos.y == 0 || pos.y == gridHeight - 1; + } + + private Vector2Int GetNextPathPoint(Vector2Int current, Vector2Int target) + { + // Calculate direction to target + Vector2Int direction = target - current; + float distance = direction.magnitude; + + // List of possible next points + List possibleMoves = new List(); + + // Always try to move closer to the target when possible + if (Mathf.Abs(direction.x) > Mathf.Abs(direction.y)) + { + // Prioritize horizontal movement + if (direction.x > 0) + possibleMoves.Add(current + Vector2Int.right); + else + possibleMoves.Add(current + Vector2Int.left); + } + else + { + // Prioritize vertical movement + if (direction.y > 0) + possibleMoves.Add(current + Vector2Int.up); + else + possibleMoves.Add(current + Vector2Int.down); + } + + // Add some randomness to prevent straight lines + if (UnityEngine.Random.value < 0.4f) + { + if (current.x > 1) + possibleMoves.Add(current + Vector2Int.left); + if (current.x < gridWidth - 2) + possibleMoves.Add(current + Vector2Int.right); + } + + // Filter out invalid moves + possibleMoves.RemoveAll(move => + IsOutOfBounds(move) || + mainPath.Contains(move) || + IsBorderPosition(move) + ); + + // If no valid moves, try moving directly towards target + if (possibleMoves.Count == 0) + { + Vector2Int directMove = current + new Vector2Int( + Mathf.Clamp(direction.x, -1, 1), + Mathf.Clamp(direction.y, -1, 1) + ); + + if (!IsOutOfBounds(directMove) && + !mainPath.Contains(directMove) && + !IsBorderPosition(directMove)) + { + return directMove; + } + } + + // Return random valid move, or current position if no valid moves + return possibleMoves.Count > 0 ? + possibleMoves[UnityEngine.Random.Range(0, possibleMoves.Count)] : + current; + } + + // Add this to help with spawning + public Vector2Int GetRandomSpawnPoint() + { + return possibleSpawnPoints[UnityEngine.Random.Range(0, possibleSpawnPoints.Count)]; + } + + // Add this to help with placing the exit + public Vector2Int GetRandomExitPoint() + { + return possibleExitPoints[UnityEngine.Random.Range(0, possibleExitPoints.Count)]; + } + + // Enhanced debug visualization + void OnDrawGizmos() + { + if (!Application.isPlaying || !enforceMainPath) return; + + // Draw start point + Gizmos.color = Color.green; + Gizmos.DrawWireSphere( + new Vector3(startPoint.x * chunkSizeX, 1, startPoint.y * chunkSizeZ), + 1.5f + ); + + // Draw end point + Gizmos.color = Color.red; + Gizmos.DrawWireSphere( + new Vector3(endPoint.x * chunkSizeX, 1, endPoint.y * chunkSizeZ), + 1.5f + ); + + // Draw main path + Gizmos.color = Color.yellow; + for (int i = 0; i < mainPath.Count - 1; i++) + { + Vector3 start = new Vector3(mainPath[i].x * chunkSizeX, 1, mainPath[i].y * chunkSizeZ); + Vector3 end = new Vector3(mainPath[i + 1].x * chunkSizeX, 1, mainPath[i + 1].y * chunkSizeZ); + Gizmos.DrawLine(start, end); + Gizmos.DrawWireSphere(start, 0.5f); + } + + // Draw branch connections + Gizmos.color = Color.cyan; + foreach (var point in guaranteedConnections) + { + if (!mainPath.Contains(point)) + { + Gizmos.DrawWireSphere( + new Vector3(point.x * chunkSizeX, 1, point.y * chunkSizeZ), + 0.5f + ); + } + } + } + void InitializeGrid(List allPrefabs) { grid = new WaveCell[gridWidth, gridHeight]; @@ -91,7 +327,7 @@ public class WFCGenerator : MonoBehaviour }*/ - + // Restrict edge cells to prefabs with walls or closed connections List FilterPrefabsForBorders(int x, int y, List possiblePrefabs) @@ -455,57 +691,54 @@ public class WFCGenerator : MonoBehaviour } } - // Helper: Filter prefabs based on compatibility - /*List FilterPrefabsForNeighbor(WaveCell neighborCell, Direction dir, ConnectionSocket requiredSocket) + protected List FilterPrefabsForNeighbor(WaveCell neighborCell, Direction dir, ConnectionSocket requiredSocket) { - // Log valid prefabs from the matrix - // Get the OPPOSITE direction for the neighbor's required socket - Direction neighborRequiredDir = ConnectionCompatibilityHelpers.GetOppositeDirection(dir); - - // Fetch valid prefabs from the compatibility matrix - if (chunkCompatibilityMatrix.compatibilityMatrix.TryGetValue(neighborRequiredDir, out var socketDict) && - socketDict.TryGetValue(requiredSocket, out var validPrefabs)) - { - // Intersect with the neighbor's current possible prefabs - List intersection = validPrefabs.Intersect(neighborCell.PossiblePrefabs).ToList(); - - for (int i = intersection.Count - 1; i >= 0; i--) - { - if (!IsBorderCellPosition(neighborCell)) - if (intersection[i].GetComponent().IsBorderPrefab()) - intersection.RemoveAt(i); - } - if (intersection.Count > 0) - return intersection; - } - - Debug.LogError($"No prefabs found for {neighborRequiredDir} direction and socket {requiredSocket}!"); - return new List(); - }*/ - - List FilterPrefabsForNeighbor(WaveCell neighborCell, Direction dir, ConnectionSocket requiredSocket) - { - // Use the neighbor direction directly since the matrix is keyed by neighbor direction. if (chunkCompatibilityMatrix.compatibilityMatrix.TryGetValue(dir, out var socketDict) && socketDict.TryGetValue(requiredSocket, out var validPrefabs)) { - // Intersect with the neighbor's current possible prefabs List intersection = validPrefabs.Intersect(neighborCell.PossiblePrefabs).ToList(); + // Remove border prefabs from non-border positions for (int i = intersection.Count - 1; i >= 0; i--) { if (!IsBorderCellPosition(neighborCell)) - if (intersection[i].GetComponent().IsBorderPrefab()) + { + var connData = intersection[i].GetComponent(); + if (connData.IsBorderPrefab()) intersection.RemoveAt(i); + // If this is a guaranteed connection point, remove prefabs that don't have floor connections + else if (guaranteedConnections.Contains(neighborCell.GridPosition)) + { + bool hasFloorConnection = false; + switch (dir) + { + case Direction.North: + hasFloorConnection = connData.NorthConnection == ConnectionSocket.floor; + break; + case Direction.South: + hasFloorConnection = connData.SouthConnection == ConnectionSocket.floor; + break; + case Direction.East: + hasFloorConnection = connData.EastConnection == ConnectionSocket.floor; + break; + case Direction.West: + hasFloorConnection = connData.WestConnection == ConnectionSocket.floor; + break; + } + if (!hasFloorConnection) + intersection.RemoveAt(i); + } + } } + if (intersection.Count > 0) return intersection; } - Debug.LogError($"No prefabs found for {dir} direction and socket {requiredSocket}!"); return new List(); } + void InstantiatePrefabs() { for (int x = 0; x < gridWidth; x++)