Thank you very much for another outstanding video Ketra Games! One thing I noticed is that you are using LTS version 2022.3.4f1 I vaguely recall that there was a bug with the UI Canvas in versions 2022.3.3f1 and 2022.3.4f1, so you may want to upgrade to 5f1 or higher when you can, since the bug should be fixed by then.
Could you make a tutorial that has the player start at a randomly generated start point (preferably as far away from the exist as possible) or the ability to put a predetermined spawnpoint as well as a randomly generated end point like at the end of the maze generatation spawn a door or chest or such?
I searched online and it seems this is one of the most recent pieces of content about the Nav Mesh in Unity. It seems not too many people are discussing the Nav Mesh these days. Tutorials less then a year back seem outdated. It’s hard to find Nav Mesh content and I have so many questions.
Hi, there, I'm currently building my own maze with my own setup, for starters my own maze has the floor inside the mazecellprefab so It spawns the floor for each mazecell, you have a seperate 'floor' gameobject. While I have my floor game object within the mazecellprefab, I tried adding the navmeshsurface to the floor prefab in my mazecellprefab hoping the navmesh will automatically just creat itself as the maze spawns, but that doesn't work. Please could anyone help me? @ketragames
This is my own MazeGenerator script: using System; using System.Collections.Generic; using Unity.AI.Navigation; using UnityEngine; using Random = UnityEngine.Random; public class MazeGenerator : MonoBehaviour { [Range(5, 500)] // 500, The higher the number the longer the maze will generate public int mazeWidth = 5, mazeHeight = 5; // The dimensions of the maze public int startX, startY; // The position our algorithm will start from. MazeCell[,] maze; // An array of maze cells representing the maze grid Vector2Int currentCell; // The maze cell we are currently looking at public MazeCell[,] GetMaze() { // Logic for generating the maze GenerateMaze(); GetComponent().BuildNavMesh(); return maze; } void GenerateMaze() { maze = new MazeCell[mazeWidth, mazeHeight]; for (int x = 0; x < mazeWidth; x++) { for (int y = 0; y < mazeHeight; y++) { maze[x, y] = new MazeCell(x, y); } } // Start carving our maze path CarvePath(startX, startY); // Log a message when the maze generation is complete Debug.Log("Maze Generation Completed!"); } List directions = new List { Direction.Up, Direction.Down, Direction.Left, Direction.Right,
}; List GetRandomDirections() { // Make a copy of our directions list that we can mess around with List dir = new List(directions); // Make a directions list to put our randomised directions into List rndDir = new List(); while (dir.Count > 0) { int rnd = Random.Range(0, dir.Count); // Get random index in list rndDir.Add(dir[rnd]); // Add the random direction to our list dir.RemoveAt(rnd); // Remove that direction so we can't choose it again } // When we've got all four directions in a random order, return the queue return rndDir; } bool IsCellValid (int x, int y) {
// If the cell is outside of the map or has already been visited, we consider it not valid if (x < 0 || y < 0 || x > mazeWidth - 1 || y > mazeHeight - 1 || maze[x, y].visited) return false; else return true; } Vector2Int CheckNeighbours () {
List rndDir = GetRandomDirections(); for (int i = 0; i < rndDir.Count; i++) {
// Set neighbour coordinates to current cell for now Vector2Int neighbour = currentCell; switch (rndDir[i]) { case Direction.Up: neighbour.y++; break; case Direction.Down: neighbour.y--; break; case Direction.Right: neighbour.x++; break; case Direction.Left: neighbour.x--; break; } // if the neighbour we just tried is valid, we can return that neighbour. If not, we go again if (IsCellValid(neighbour.x, neighbour.y)) return neighbour;
} // we tried all directions and didn't fina a valid neighbour, we return the currentCell values return currentCell; } void BreakWalls ( Vector2Int primaryCell, Vector2Int secondaryCell) { // We can only go in one direction at a time so we can handle this using if else statements if (primaryCell.x > secondaryCell.x) { // Primary Cell is Left Wall
} } // Starting at the x, y passed in, carves a path through the maze until it encounters a "dead end" // ( A dead end is a cell with no valid neighbours) void CarvePath (int x, int y) { // Perform a quick check to make sure our start position is within the boundaries of the map // if not, set them to a default (I'm using 0) and throw a little warning up. if (x < 0 || y < 0 || x > mazeWidth - 1 || y > mazeHeight - 1) { x = y = 0; Debug.LogWarning("Starting position is out of bounds, defaulting to 0, 0"); } // Set current cell to the starting position we were passed currentCell = new Vector2Int(x, y); // A list to keep track of our current path List path = new List(); // Loop until we encounter a dead end bool deadEnd = false; while (!deadEnd) {
// Get the next cell we're going to try Vector2Int nextCell = CheckNeighbours(); // If that cell has no valid neighbhours, set deadend to true so we break out of the loop if (nextCell == currentCell) {
// If that cell has no valid neighbours, set deadend to true so we break out of the loop for (int i = path.Count - 1; i >= 0; i--) {
currentCell = path[i]; // Set currentCell to the next step back along our path path.RemoveAt(i); // Remove this step from the path nextCell = CheckNeighbours(); // Check that cell to see if any other neighbouts are valid // If we find a valid neighbour, break out of the loop if (nextCell != currentCell) break; } if (nextCell == currentCell) deadEnd = true;
} else {
BreakWalls(currentCell, nextCell); // Set wall flags on these two cells maze[currentCell.x, currentCell.y].visited = true; // Set cell to visited before moving on currentCell = nextCell; // Set current cell to the valid neighbour we fond path.Add(currentCell); // Add this cell to our path } } } } public enum Direction { Up, Down, Left, Right } public class MazeCell { public bool visited; public int x, y; public bool topWall; public bool leftWall; // Return x and y as a Vector2Int for convenience sake public Vector2Int position { get { return new Vector2Int(x, y);
} } public MazeCell (int x, int y) { // The coordinates of this cell in the maze grid this.x = x; this.y = y; // Whethere the algorithm has visited this cell or not - false to start visited = false; // All walls are present until the algorirthm removes them topWall = leftWall = true; } }
The Nav in Unity used to be such a pain, I'm happy they're still updating it. Thanks, I didn't know the extent.
👍😊
This looks like a good approach for a rogue-like
How have u not got more views simple easy to follow instructions
The YT algorithm
wow thanks so much, straight to the point and well explained!
Thanks very much for this comment 😊
Thank you very much for another outstanding video Ketra Games!
One thing I noticed is that you are using LTS version 2022.3.4f1
I vaguely recall that there was a bug with the UI Canvas in versions 2022.3.3f1 and 2022.3.4f1, so you may want to upgrade to 5f1 or higher when you can, since the bug should be fixed by then.
Excellent video, I really liked it, but missing an opening to the end of the maze, how would I do that?
Could you make a tutorial that has the player start at a randomly generated start point (preferably as far away from the exist as possible) or the ability to put a predetermined spawnpoint as well as a randomly generated end point like at the end of the maze generatation spawn a door or chest or such?
I searched online and it seems this is one of the most recent pieces of content about the Nav Mesh in Unity. It seems not too many people are discussing the Nav Mesh these days. Tutorials less then a year back seem outdated. It’s hard to find Nav Mesh content and I have so many questions.
Please make a video on moving platform with navmesh surface.
hi can we do it in unreal with blueprint ?
Top down tutorial!!!!
Ive tried to watch your videos many times, but can not do it. Adhd attention span makes it difficult. Oh wel
Sorry to hear this 😢
if you are explaining NavMesh why are you confusing others with scaling issues and maze generating stuff! i just dont get it!
Sorry for the confusion. This is part of a series on procedural generation and continues from the previous video where the maze was generated
Hi, there, I'm currently building my own maze with my own setup, for starters my own maze has the floor inside the mazecellprefab so It spawns the floor for each mazecell, you have a seperate 'floor' gameobject. While I have my floor game object within the mazecellprefab, I tried adding the navmeshsurface to the floor prefab in my mazecellprefab hoping the navmesh will automatically just creat itself as the maze spawns, but that doesn't work. Please could anyone help me? @ketragames
This is my own MazeGenerator script:
using System;
using System.Collections.Generic;
using Unity.AI.Navigation;
using UnityEngine;
using Random = UnityEngine.Random;
public class MazeGenerator : MonoBehaviour {
[Range(5, 500)] // 500, The higher the number the longer the maze will generate
public int mazeWidth = 5, mazeHeight = 5; // The dimensions of the maze
public int startX, startY; // The position our algorithm will start from.
MazeCell[,] maze; // An array of maze cells representing the maze grid
Vector2Int currentCell; // The maze cell we are currently looking at
public MazeCell[,] GetMaze()
{
// Logic for generating the maze
GenerateMaze();
GetComponent().BuildNavMesh();
return maze;
}
void GenerateMaze()
{
maze = new MazeCell[mazeWidth, mazeHeight];
for (int x = 0; x < mazeWidth; x++)
{
for (int y = 0; y < mazeHeight; y++)
{
maze[x, y] = new MazeCell(x, y);
}
}
// Start carving our maze path
CarvePath(startX, startY);
// Log a message when the maze generation is complete
Debug.Log("Maze Generation Completed!");
}
List directions = new List {
Direction.Up, Direction.Down, Direction.Left, Direction.Right,
};
List GetRandomDirections() {
// Make a copy of our directions list that we can mess around with
List dir = new List(directions);
// Make a directions list to put our randomised directions into
List rndDir = new List();
while (dir.Count > 0) {
int rnd = Random.Range(0, dir.Count); // Get random index in list
rndDir.Add(dir[rnd]); // Add the random direction to our list
dir.RemoveAt(rnd); // Remove that direction so we can't choose it again
}
// When we've got all four directions in a random order, return the queue
return rndDir;
}
bool IsCellValid (int x, int y) {
// If the cell is outside of the map or has already been visited, we consider it not valid
if (x < 0 || y < 0 || x > mazeWidth - 1 || y > mazeHeight - 1 || maze[x, y].visited) return false;
else return true;
}
Vector2Int CheckNeighbours () {
List rndDir = GetRandomDirections();
for (int i = 0; i < rndDir.Count; i++) {
// Set neighbour coordinates to current cell for now
Vector2Int neighbour = currentCell;
switch (rndDir[i]) {
case Direction.Up:
neighbour.y++;
break;
case Direction.Down:
neighbour.y--;
break;
case Direction.Right:
neighbour.x++;
break;
case Direction.Left:
neighbour.x--;
break;
}
// if the neighbour we just tried is valid, we can return that neighbour. If not, we go again
if (IsCellValid(neighbour.x, neighbour.y)) return neighbour;
}
// we tried all directions and didn't fina a valid neighbour, we return the currentCell values
return currentCell;
}
void BreakWalls ( Vector2Int primaryCell, Vector2Int secondaryCell) {
// We can only go in one direction at a time so we can handle this using if else statements
if (primaryCell.x > secondaryCell.x) { // Primary Cell is Left Wall
maze[primaryCell.x, primaryCell.y].leftWall = false;
} else if (primaryCell.x < secondaryCell.x) { // Secondary Cell is Left Wall
maze[secondaryCell.x, secondaryCell.y].leftWall = false;
} else if (primaryCell.y < secondaryCell.y) { // Primary Cell is Top Wall
maze[primaryCell.x, primaryCell.y].topWall = false;
} else if (primaryCell.y > secondaryCell.y) { // Secondary Cell is Top Wall
maze[secondaryCell.x, secondaryCell.y].topWall = false;
}
}
// Starting at the x, y passed in, carves a path through the maze until it encounters a "dead end"
// ( A dead end is a cell with no valid neighbours)
void CarvePath (int x, int y) {
// Perform a quick check to make sure our start position is within the boundaries of the map
// if not, set them to a default (I'm using 0) and throw a little warning up.
if (x < 0 || y < 0 || x > mazeWidth - 1 || y > mazeHeight - 1) {
x = y = 0;
Debug.LogWarning("Starting position is out of bounds, defaulting to 0, 0");
}
// Set current cell to the starting position we were passed
currentCell = new Vector2Int(x, y);
// A list to keep track of our current path
List path = new List();
// Loop until we encounter a dead end
bool deadEnd = false;
while (!deadEnd) {
// Get the next cell we're going to try
Vector2Int nextCell = CheckNeighbours();
// If that cell has no valid neighbhours, set deadend to true so we break out of the loop
if (nextCell == currentCell) {
// If that cell has no valid neighbours, set deadend to true so we break out of the loop
for (int i = path.Count - 1; i >= 0; i--) {
currentCell = path[i]; // Set currentCell to the next step back along our path
path.RemoveAt(i); // Remove this step from the path
nextCell = CheckNeighbours(); // Check that cell to see if any other neighbouts are valid
// If we find a valid neighbour, break out of the loop
if (nextCell != currentCell) break;
}
if (nextCell == currentCell)
deadEnd = true;
} else {
BreakWalls(currentCell, nextCell); // Set wall flags on these two cells
maze[currentCell.x, currentCell.y].visited = true; // Set cell to visited before moving on
currentCell = nextCell; // Set current cell to the valid neighbour we fond
path.Add(currentCell); // Add this cell to our path
}
}
}
}
public enum Direction {
Up,
Down,
Left,
Right
}
public class MazeCell {
public bool visited;
public int x, y;
public bool topWall;
public bool leftWall;
// Return x and y as a Vector2Int for convenience sake
public Vector2Int position {
get {
return new Vector2Int(x, y);
}
}
public MazeCell (int x, int y) {
// The coordinates of this cell in the maze grid
this.x = x;
this.y = y;
// Whethere the algorithm has visited this cell or not - false to start
visited = false;
// All walls are present until the algorirthm removes them
topWall = leftWall = true;
}
}