almost working guaranteed path

This commit is contained in:
Pedro Gomes 2025-02-07 18:59:41 +00:00
parent 4f781ab83e
commit c59fd63cb5
2 changed files with 294 additions and 54 deletions

View File

@ -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:

View File

@ -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<Vector2Int> possibleSpawnPoints = new List<Vector2Int>();
private List<Vector2Int> possibleExitPoints = new List<Vector2Int>();
bool broken = false;
[SerializeField] private bool enforceMainPath = true;
private List<Vector2Int> mainPath = new List<Vector2Int>();
private HashSet<Vector2Int> guaranteedConnections = new HashSet<Vector2Int>();
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<Vector2Int> possibleMoves = new List<Vector2Int>();
// 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<GameObject> 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<GameObject> FilterPrefabsForBorders(int x, int y, List<GameObject> possiblePrefabs)
@ -455,57 +691,54 @@ public class WFCGenerator : MonoBehaviour
}
}
// Helper: Filter prefabs based on compatibility
/*List<GameObject> FilterPrefabsForNeighbor(WaveCell neighborCell, Direction dir, ConnectionSocket requiredSocket)
protected List<GameObject> 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<GameObject> intersection = validPrefabs.Intersect(neighborCell.PossiblePrefabs).ToList();
for (int i = intersection.Count - 1; i >= 0; i--)
{
if (!IsBorderCellPosition(neighborCell))
if (intersection[i].GetComponent<ChunkConnectionData>().IsBorderPrefab())
intersection.RemoveAt(i);
}
if (intersection.Count > 0)
return intersection;
}
Debug.LogError($"No prefabs found for {neighborRequiredDir} direction and socket {requiredSocket}!");
return new List<GameObject>();
}*/
List<GameObject> 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<GameObject> 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<ChunkConnectionData>().IsBorderPrefab())
{
var connData = intersection[i].GetComponent<ChunkConnectionData>();
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<GameObject>();
}
void InstantiatePrefabs()
{
for (int x = 0; x < gridWidth; x++)