diff --git a/Assets/Scenes/Scene.unity b/Assets/Scenes/Scene.unity index 5e29b6f..230d5ad 100644 --- a/Assets/Scenes/Scene.unity +++ b/Assets/Scenes/Scene.unity @@ -1368,6 +1368,7 @@ GameObject: - component: {fileID: 1200728390} - component: {fileID: 1200728389} - component: {fileID: 1200728392} + - component: {fileID: 1200728394} - component: {fileID: 1200728391} - component: {fileID: 1200728393} m_Layer: 0 @@ -1389,7 +1390,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 78f495c1a5b3b4956a833d0580ea6984, type: 3} m_Name: m_EditorClassIdentifier: - generator: {fileID: 1200728392} + generator: {fileID: 1200728394} spawner: {fileID: 1200728391} Size: 100 --- !u!4 &1200728390 @@ -1453,6 +1454,18 @@ MonoBehaviour: SceneMigrationSynchronization: 1 DontDestroyWithOwner: 0 AutoObjectParentSync: 1 +--- !u!114 &1200728394 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1200728388} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 83358193f444d457aa5e95796c768597, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &1200768371 GameObject: m_ObjectHideFlags: 0 @@ -2029,6 +2042,7 @@ GameObject: - component: {fileID: 1536468913} - component: {fileID: 1536468912} - component: {fileID: 1536468911} + - component: {fileID: 1536468915} m_Layer: 0 m_Name: Preview m_TagString: Untagged @@ -2075,7 +2089,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: bbb394441ceed4c9da13fcaf6237191e, type: 3} m_Name: m_EditorClassIdentifier: - generator: {fileID: 1536468912} + generator: {fileID: 1536468915} spawner: {fileID: 1536468911} Size: 100 --- !u!4 &1536468914 @@ -2093,6 +2107,18 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 10 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1536468915 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1536468910} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 83358193f444d457aa5e95796c768597, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &1556916251 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Src/GameManager.cs b/Assets/Src/GameManager.cs index f81c1ff..da5ff36 100644 --- a/Assets/Src/GameManager.cs +++ b/Assets/Src/GameManager.cs @@ -49,7 +49,7 @@ public void OnCheese() { Vector2Int? currentCheesePos = cheese ? Maze.WorldPosToCoords(cheese.transform.position) : null; var cheesePos = generator.GenerateCheese(maze, getPlayerPosiitons(), currentCheesePos); - cheese = spawner.SpawnCheese(maze, new Vector2Int(5, 5)); + cheese = spawner.SpawnCheese(maze, cheesePos); } public Vector3[] getPlayerPosiitons() diff --git a/Assets/Src/Maze/MazeSpawner.cs b/Assets/Src/Maze/MazeSpawner.cs index ff3c94d..d483ea6 100644 --- a/Assets/Src/Maze/MazeSpawner.cs +++ b/Assets/Src/Maze/MazeSpawner.cs @@ -33,7 +33,8 @@ public void SpawnMaze(Maze m) public void SpawnObstacle(int x, int y, int size) { var pos = new Vector3(x - maze.GetCoordOffset() + gridOffset, 0, y - maze.GetCoordOffset() + gridOffset); - GameObject obstacle = Instantiate(BlockPrefab, pos, new Quaternion(), MazeParent.transform); + GameObject obstacle = Instantiate(BlockPrefab, pos, new Quaternion()); + obstacle.transform.parent = MazeParent.transform; NetworkObject network = obstacle.GetComponent(); if (network != null) { @@ -44,7 +45,7 @@ public void SpawnObstacle(int x, int y, int size) public GameObject SpawnCheese(Maze maze, Vector2Int coords) { var pos = new Vector3(coords.x - maze.GetCoordOffset() + gridOffset, 0, coords.y - maze.GetCoordOffset() + gridOffset); - var cheese = Instantiate(CheesePrefab, pos, new Quaternion(), MazeParent.transform); + var cheese = Instantiate(CheesePrefab, pos, new Quaternion()); cheese.GetComponent().Spawn(); return cheese; } diff --git a/Assets/Src/Maze/PrimsMaze.meta b/Assets/Src/Maze/PrimsMaze.meta new file mode 100644 index 0000000..4909e93 --- /dev/null +++ b/Assets/Src/Maze/PrimsMaze.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 35872523f520d46efa3ee4b0eb869aae +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Src/Maze/PrimsMaze/PrimsMazeGenerator.cs b/Assets/Src/Maze/PrimsMaze/PrimsMazeGenerator.cs new file mode 100644 index 0000000..b7060f1 --- /dev/null +++ b/Assets/Src/Maze/PrimsMaze/PrimsMazeGenerator.cs @@ -0,0 +1,174 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +public class PrimsMazeGenerator : Generator +{ + + private bool isCheeseArea(int x, int y) + { + return x > 90 && y > 90; + } + + private bool isSpawnArea(int x, int y) + { + return x < 10 && y < 10; + } + + private bool isEdge(int x, int y, int size) + { + return x == 0 || y == 0 || x == size - 1 || y == size - 1; + } + + public override Vector2Int GenerateCheese(Maze maze, Vector3[] playerPositions, Vector2Int? currentCheese) + { + return new Vector2Int(95, 95); + } + + public override Maze GenerateMaze(int size) + { + var maze = new Maze(size); + + // Fill the maze with walls + for (int x = 0; x < size; x++) + { + for (int y = 0; y < size; y++) + { + maze.Set(x, y, true); + } + } + // initial cells + Vector2Int startCell = new Vector2Int(5, 5); + Vector2Int goalCell = new Vector2Int(95, 95); + + // + // Random passage generation + // + + + + + // A random cell is selected to be the origin of the generated maze, the initial passage cell + maze.Set(startCell.x, startCell.y, false); + + + // Create a list to store all of the frontier cells and calculte the first ones of the intial passage + HashSet<(int, int)> frontierCells = GetNeighborCells(startCell.x, startCell.y, true, maze); + + // While there's frontier cells, continue creating new passages + while (frontierCells.Any()) + { + // Select a random frontier cell and change it into a passage + int randomIndex = Random.Range(0, frontierCells.Count); + (int, int) randomFrontierCell = frontierCells.ElementAt(randomIndex); + int randomFrontierCellX = randomFrontierCell.Item1; + int randomFrontierCellY = randomFrontierCell.Item2; + maze.Set(randomFrontierCellX, randomFrontierCellY, false); + + // Obtain the list of passage cells which can be connected to the selected frontier cell + HashSet<(int, int)> candidateCells = GetNeighborCells(randomFrontierCellX, randomFrontierCellY, false, maze); + if (candidateCells.Any()) // safety statement + { + // Select a random passage to connect with the frontier cell + int randomIndexCandidate = Random.Range(0, candidateCells.Count); + (int, int) randomCellConnection = candidateCells.ElementAt(randomIndexCandidate); + int randomCellConnectionX = randomCellConnection.Item1; + int randomCellConnectionY = randomCellConnection.Item2; + + // Calculate which cell is inbetween the frontier cell and the passage + (int, int) cellBetween; + if (randomFrontierCellX < randomCellConnectionX) + cellBetween = (randomFrontierCellX + 1, randomFrontierCellY); + else if (randomFrontierCellX > randomCellConnectionX) + cellBetween = (randomFrontierCellX - 1, randomFrontierCellY); + else + { + if (randomFrontierCellY < randomCellConnectionY) + cellBetween = (randomFrontierCellX, randomFrontierCellY + 1); + else + cellBetween = (randomFrontierCellX, randomFrontierCellY - 1); + } + + // Make the cell in between a passage to connect the frontier cell and passage cell + maze.Set(cellBetween.Item1, cellBetween.Item2, false); + } + + // Remove the frontier cell that has been converted to a passage + frontierCells.Remove(randomFrontierCell); + + // Calculate the frontier cells from the previous frontier cell selected + frontierCells.UnionWith(GetNeighborCells(randomFrontierCellX, randomFrontierCellY, true, maze)); + } + + + // + // End of random passage generation + // + + // DO NOT TOUCH + // Fill in the spawn and cheese areas + for (int x = 0; x < size; x++) + { + for (int y = 0; y < size; y++) + { + if (isEdge(x, y, size)) + { + maze.Set(x, y, true); + continue; + } + if (isSpawnArea(x, y) || isCheeseArea(x, y)) + { + maze.Set(x, y, false); + continue; + } + } + } + + return maze; + } + + private HashSet<(int, int)> GetNeighborCells(int x, int y, bool checkFrontier, Maze actualMaze) + { + HashSet<(int, int)> newNeighborCells = new HashSet<(int, int)>(); + + // Check if the given cell can have a neighbor cell in the different axis + // A neighbor cell can't be outside the boundaries of the 2d array or belong to the outer edge. + // It also makes a different check depending on whether we are looking for the frontier cells or the possible passage cells + if (x > 2) + { + var cellToCheck = new Vector2Int(x - 2, y); + if (checkFrontier ? actualMaze.GetTile(cellToCheck) == true : actualMaze.GetTile(cellToCheck) == false) + { + newNeighborCells.Add((cellToCheck.x, cellToCheck.y)); + } + } + if (x < actualMaze.Size - 3) + { + var cellToCheck = new Vector2Int(x + 2, y); + if (checkFrontier ? actualMaze.GetTile(cellToCheck) == true : actualMaze.GetTile(cellToCheck) == false) + { + newNeighborCells.Add((cellToCheck.x, cellToCheck.y)); + } + } + + if (y > 2) + { + var cellToCheck = new Vector2Int(x, y - 2); + + if (checkFrontier ? actualMaze.GetTile(cellToCheck) == true : actualMaze.GetTile(cellToCheck) == false) + { + newNeighborCells.Add((cellToCheck.x, cellToCheck.y)); + } + } + if (y < actualMaze.Size - 3) + { + var cellToCheck = new Vector2Int(x, y + 2); + if (checkFrontier ? actualMaze.GetTile(cellToCheck) == true : actualMaze.GetTile(cellToCheck) == false) + { + newNeighborCells.Add((cellToCheck.x, cellToCheck.y)); + } + } + + return newNeighborCells; + } +} diff --git a/Assets/Src/Maze/PrimsMaze/PrimsMazeGenerator.cs.meta b/Assets/Src/Maze/PrimsMaze/PrimsMazeGenerator.cs.meta new file mode 100644 index 0000000..ed275f4 --- /dev/null +++ b/Assets/Src/Maze/PrimsMaze/PrimsMazeGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 83358193f444d457aa5e95796c768597 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: