- Home /
How can i make an entrance and exit in this maze ?
The script build a maze but it's close i can move the player inside. I want that each time it's creating the maze it will create a random 2 empty bricks in the outer square like entrance and exit.
So i want to add some public variable so if i change it's value to 1 there will be one entrance and one exit. If i will change the value 3 or to 15 so there will be 3 entrances and 3 exits or if i change it to 15 there will be 15 entrances and 15 exits.
This kind of logic or something like that. And that this entrances and exits will be in a random positions each time i'm running the game.
Another sub question:
I can change now the height and width of the maze. but if i want that all the terrain will be filled with the maze ? for example the terrain resolution is:
Example how the maze looks like now:
Width = 500 Length = 500 Height = 600
So i want that the maze will be automatic all over the terrain area. The Height should not be 600 but the maze size should be on the terrain size.
// mazegen.cs
// remember you can NOT have even numbers of height or width in this style of block maze
// to ensure we can get walls around all tunnels... so use 21 x 13 , or 7 x 7 for examples.
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
public class MazeGenerator : MonoBehaviour
{
public int width, height;
public Material brick;
private int[,] Maze;
private Stack<Vector2> _tiletoTry = new Stack<Vector2>();
private List<Vector2> offsets = new List<Vector2> { new Vector2(0, 1), new Vector2(0, -1), new Vector2(1, 0), new Vector2(-1, 0) };
private System.Random rnd = new System.Random();
private Vector2 _currentTile;
public static string MazeString;
public Vector2 CurrentTile
{
get { return _currentTile; }
private set
{
if (value.x < 1 || value.x >= this.width - 1 || value.y < 1 || value.y >= this.height - 1)
{
throw new ArgumentException("Width and Height must be greater than 2 to make a maze");
}
_currentTile = value;
}
}
private static MazeGenerator instance;
public static MazeGenerator Instance
{
get { return instance; }
}
void Awake() { instance = this; MakeBlocks(); }
// end of main program
// ============= subroutines ============
void MakeBlocks()
{
Maze = new int[width, height];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Maze[x, y] = 1;
}
}
CurrentTile = Vector2.one;
_tiletoTry.Push(CurrentTile);
Maze = CreateMaze(); // generate the maze in Maze Array.
GameObject ptype = null;
for (int i = 0; i <= Maze.GetUpperBound(0); i++)
{
for (int j = 0; j <= Maze.GetUpperBound(1); j++)
{
if (Maze[i, j] == 1)
{
MazeString = MazeString + "X"; // added to create String
ptype = GameObject.CreatePrimitive(PrimitiveType.Cube);
ptype.transform.position = new Vector3(i * ptype.transform.localScale.x, 0, j * ptype.transform.localScale.z);
if (brick != null) { ptype.GetComponent<Renderer>().material = brick; }
ptype.transform.parent = transform;
}
else if (Maze[i, j] == 0)
{
MazeString = MazeString + "."; // added to create String
}
}
MazeString = MazeString + "\n"; // added to create String
}
print(MazeString); // added to create String
}
// =======================================
public int[,] CreateMaze()
{
//local variable to store neighbors to the current square as we work our way through the maze
List<Vector2> neighbors;
//as long as there are still tiles to try
while (_tiletoTry.Count > 0)
{
//excavate the square we are on
Maze[(int)CurrentTile.x, (int)CurrentTile.y] = 0;
//get all valid neighbors for the new tile
neighbors = GetValidNeighbors(CurrentTile);
//if there are any interesting looking neighbors
if (neighbors.Count > 0)
{
//remember this tile, by putting it on the stack
_tiletoTry.Push(CurrentTile);
//move on to a random of the neighboring tiles
CurrentTile = neighbors[rnd.Next(neighbors.Count)];
}
else
{
//if there were no neighbors to try, we are at a dead-end toss this tile out
//(thereby returning to a previous tile in the list to check).
CurrentTile = _tiletoTry.Pop();
}
}
print("Maze Generated ...");
return Maze;
}
// ================================================
// Get all the prospective neighboring tiles "centerTile" The tile to test
// All and any valid neighbors</returns>
private List<Vector2> GetValidNeighbors(Vector2 centerTile)
{
List<Vector2> validNeighbors = new List<Vector2>();
//Check all four directions around the tile
foreach (var offset in offsets)
{
//find the neighbor's position
Vector2 toCheck = new Vector2(centerTile.x + offset.x, centerTile.y + offset.y);
//make sure the tile is not on both an even X-axis and an even Y-axis
//to ensure we can get walls around all tunnels
if (toCheck.x % 2 == 1 || toCheck.y % 2 == 1)
{
//if the potential neighbor is unexcavated (==1)
//and still has three walls intact (new territory)
if (Maze[(int)toCheck.x, (int)toCheck.y] == 1 && HasThreeWallsIntact(toCheck))
{
//add the neighbor
validNeighbors.Add(toCheck);
}
}
}
return validNeighbors;
}
// ================================================
// Counts the number of intact walls around a tile
//"Vector2ToCheck">The coordinates of the tile to check
//Whether there are three intact walls (the tile has not been dug into earlier.
private bool HasThreeWallsIntact(Vector2 Vector2ToCheck)
{
int intactWallCounter = 0;
//Check all four directions around the tile
foreach (var offset in offsets)
{
//find the neighbor's position
Vector2 neighborToCheck = new Vector2(Vector2ToCheck.x + offset.x, Vector2ToCheck.y + offset.y);
//make sure it is inside the maze, and it hasn't been dug out yet
if (IsInside(neighborToCheck) && Maze[(int)neighborToCheck.x, (int)neighborToCheck.y] == 1)
{
intactWallCounter++;
}
}
//tell whether three walls are intact
return intactWallCounter == 3;
}
// ================================================
private bool IsInside(Vector2 p)
{
//return p.x >= 0 p.y >= 0 p.x < width p.y < height;
return p.x >= 0 && p.y >= 0 && p.x < width && p.y < height;
}
}
Answer by Patxiku · Feb 24, 2017 at 08:51 AM
Answering to your terrain sized maze question, you could just get terrains size parameters and assign them to your maze height and width (Assuming 1 unit of your maze matches 1 unit of unity):
//Checking if the terrain size is even, so if it's not the value becomes even
width = (int)terrainObject.GetComponent<Terrain> ().terrainData.size.x % 2 == 0 ? (int)terrainObject.GetComponent<Terrain> ().terrainData.size.x : (int)terrainObject.GetComponent<Terrain> ().terrainData.size.x + 1;
heigth = (int)terrainObject.GetComponent<Terrain> ().terrainData.size.z % 2 == 0 ? (int)terrainObject.GetComponent<Terrain> ().terrainData.size.z : (int)terrainObject.GetComponent<Terrain> ().terrainData.size.z + 1;
For the main question, you should define some guidelanes. What do you want your maze behaviour to be?
For example:
-The entrance and exit must be on the outside.
-The entrance and exit must be accesible (have an adjacent nonwall tile).
-The entrance and exit can't be near (define a minimun distance between them).
-If more than one pair of entrance and exit exists, check that entrances and exists don't match in position.
With defined guidelines it should be easy to achieve what you are looking for, just try to reflect them into your code after the maze creation algorithm has finished.
The first part making the maze ins the size of the terrain is working. since it's taking time to build it when running the game i will stay for now with smaller size of the maze as before.
for the second part the main question. If you could guide me how to do it ? Or give me some code start with some simple tutorial how to do it ?
Let's say i want to start to do it the guidelanes in the bottom after the last function IsInside.
How do i start ?
For you have an array that represents the maze, you should be able to follow those guidelines creating rules that apply to the array.
"The entrance and exit must be on the border:" For making this possible, you must ensure that the poisition you are trying to instantiate the entrance/exis is either on the
i == 0, i ==heightofthearray, j == 0, j == widthofthearray
"The entrance and exit must be accesible (have a adjacent nonwall tile)": Just check if any of the positions that are adjacent to the position you are checking is a nonwall, you don't want to have an exit/entrance that is imposible to reach ^^.
"The entrance and exit can't be near (define a $$anonymous$$imun distance between them)": You could use the $$anonymous$$anhattan distance algorithm or any other distance algorithms to calculate the values of distance between the entrance and exit and if that value is less than the value you want, deny the exit/entrance creation.
If more than one pair of entrance and exit exists, check that entrances and exits don't match in position. Just check if there is any existing exit/entrance in the position you are looking to.
Since the maze is random the distance check using manhatten distance makes not too much sense. In a maze you can have the entrance and exit right next to each other, but the way you have to walk can be very long depending on the maze structure. So you might need some sort of path finding algorithm (A* for example) to actually trace the path.
I'm also not sure if this maze algorithm ensures an all-connectivity property or if it's possible to have isolated areas.
In either way tracing the actual path would certainly be a good idea. A* works on grids like that just fine. However to speed up the search you might want to create a node graph inside the maze and only place nodes at junktions (i.e. places that have more than 2 free neighbors or only 1 or 0 walls around).
edit
It actually should result in a fully connected maze since each path is traced from existing tiles, so they are always connected. So no matter where you place an entrance / exit they will be always connected. Of course to ensure you don't hit a wall you either have to use only odd positions on the border, or check for a wall first.
Ins$$anonymous$$d of A* you can simply use the "always go left/right" method to traverse the whole maze. Using a history stack would allow you to measure the distance directly. Since the maze has no loops you will always traverse the whole maze. So just start somewhere on the border and go ahead. Each step check if the next position is the top of the stack. It it is you're currently walking backwards so you would pop the stack each step. If not you're pushing the current position on the stack. Whenever you reach a "border position" you can simply check the current stack size and you know how many steps you need up to this point. So you can either let it run until you hit a border pos with a large enough distance, or simply let it run through the whole maze and record all distances for all border positions so you can simply pick the largest.
Your answer
Follow this Question
Related Questions
Why when creating new animator controller for the character the character is not walking right ? 0 Answers
How can i Instantiate on the terrain from left to right ? 0 Answers
Why the tile map scripts take almost all the cpu usage ? cpu usage is getting to 99% at times 1 Answer
How can i rotate object by pressing on key R and keep object facing to me my self ? 0 Answers
Why it;s never getting to the OnMouseDown function when clicking with the mouse ? 1 Answer