Advertisement
teleias

Prims

Apr 27th, 2015
258
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 12.14 KB | None | 0 0
  1. /// <information>
  2. /// Created By: Austin Takechi
  3. /// Purpose: To use Prim's Algorithm to generate
  4. ///  a randomly structured maze of dynamic proportions.
  5. /// Contact: MinoruTono@Gmail.com
  6. /// </information>
  7.  
  8. /// <summary>
  9. /// This script creates and attaches the grid,
  10. /// and also faciliates most of Prim's Algorithm.
  11. /// </summary>
  12.  
  13.  
  14. //Included by Default
  15. using UnityEngine;
  16. //Included by Default
  17. using System.Collections;
  18. //Allows us to use List< >
  19. using System.Collections.Generic;
  20.  
  21.  
  22. //This is the main Class
  23. // All of our scripting in GridScript goes in here.
  24. public class GridScript : MonoBehaviour {
  25.     //The CellPrefab is a Transform Prefab
  26.     // we use as a template to create each
  27.     // cell. This is set in Unity's Inspector.
  28.     public Transform CellPrefab;
  29.    
  30.     //Size is a Vector3, denotes the
  31.     // size of the Grid we're going to create.
  32.     // Only X and Z are used, Y is set to 1 at default.
  33.     // This is set in Unity's Inspector.
  34.     public Vector3 Size;
  35.    
  36.     //Grid[ , ] is a Multidimensional Array that
  37.     // stores each newly created Cell in an easy
  38.     // X,Z notation. 0-Based.
  39.     public Transform[,] Grid;
  40.    
  41.     // Use this for initialization
  42.     void Start () {
  43.         //CreateGrid will create a new grid of
  44.         // size X,Z; name and parent those new
  45.         // cells, and add the cell to our Grid List< >
  46.         CreateGrid();
  47.        
  48.         //SetRandomNumbers goes through each
  49.         // of our blank cells and assigns it a
  50.         // random weight.
  51.         SetRandomNumbers();
  52.        
  53.         //Set Adjacents goes through each
  54.         // of the cells and assigns the adjacents,
  55.         // and then ranks the adjacents by
  56.         // using our custom sort:
  57.         // SortByLowestWeight( )
  58.         SetAdjacents();
  59.        
  60.         //We set the entrance cell of the grid,
  61.         // by default in the lower left corner,
  62.         // or Grid[0,0].
  63.         SetStart();
  64.        
  65.         //FindNext looks for the lowest weight adjacent
  66.         // to all the cells in the Set, and adds that
  67.         // cell to the Set. A cell is disqualified if
  68.         // it has two open neighbors. This makes the
  69.         // maze full of deadends.
  70.         //FindNext also will invoke itself as soon as it
  71.         // finishes, allowing it to loop indefinitely until
  72.         // the invoke is canceled when we detect our maze is done.
  73.         FindNext();
  74.     }
  75.    
  76.     void CreateGrid(){
  77.         //Resize Grid to the proper size specified in Inspector.
  78.         // Because we use a Vector3, we need to convert the X, Y, and Z
  79.         // from Float to Int.
  80.         // We do that easily by adding "(int)" in front of the float.
  81.         // This is called explicit downcasting.
  82.         Grid = new Transform[(int)Size.x,(int)Size.z];
  83.        
  84.         //We now enter a Double For loop.
  85.         // This will create each cell by looping from x = 0 while x < Size.x,
  86.         // and then the same for z.
  87.         for(int x = 0; x < Size.x; x++){
  88.             for(int z = 0; z < Size.z; z++){
  89.                 //We create a new Transform to manipulate later.
  90.                 Transform newCell;
  91.                 //A new CellPrefab is Instantiated at the correct location
  92.                 // using "new Vector3(x,0,z)".
  93.                 // Quaternion.Identity is a rotation that we don't need to
  94.                 // worry about.
  95.                 newCell = (Transform)Instantiate(CellPrefab, new Vector3(x, 0, z), Quaternion.identity);
  96.                 //The newCell is now renamed "(x,0,z)" using String.Format
  97.                 // "(+x+",0,"+z+")" would also work, but is less efficient.
  98.                 newCell.name = string.Format("({0},0,{1})",x,z);
  99.                 //For clean-ness, we parent all the newCells to the Grid gameObject.
  100.                 newCell.parent = transform;
  101.                 //We already set the position of the newCell, but the cell's attached
  102.                 // script needs to know where it is also.
  103.                 // We assign it here.
  104.                 newCell.GetComponent<CellScript>().Position = new Vector3(x, 0, z);
  105.                 //Grid[,] keeps track of all of the cells.
  106.                 // We add the newCell to the appropriate location in the Grid array.
  107.                 Grid[x,z] = newCell;
  108.             }
  109.         }
  110.         //To keep the camera centered on the grid,
  111.         // we set the position equal to the center ( X/2 , Z/2 )
  112.         // cell in the grid. We then add Vector3.up*20f to bring
  113.         // the camera higher than the cells.
  114.         Camera.mainCamera.transform.position = Grid[(int)(Size.x/2f),(int)(Size.z/2f)].position + Vector3.up*20f;
  115.         //The orthographicSize is calculated using this
  116.         // formula.
  117.         //The higher the max of the X or Z size, the higher the camera
  118.         // is positioned.
  119.         Camera.mainCamera.orthographicSize = Mathf.Max(Size.x, Size.z);
  120.     }
  121.    
  122.     void SetRandomNumbers(){
  123.         //ForEach cell in the Grid gameObject:
  124.         foreach(Transform child in transform){
  125.             //Get a new random number between 0 and 10.
  126.             int weight = Random.Range(0,10);
  127.             //Assign that number to both the cell's text..
  128.             child.GetComponentInChildren<TextMesh>().text = weight.ToString();
  129.             //..and Weight variable in the CellScript.
  130.             child.GetComponent<CellScript>().Weight = weight;
  131.         }
  132.     }
  133.    
  134.     void SetAdjacents(){
  135.         //Double For loop acts as a ForEach
  136.         for(int x = 1; x < Size.x-1; x++){
  137.             for(int z = 1; z < Size.z-1; z++){
  138.                 //Create a new variable to be the
  139.                 // cell at position x, z.
  140.                 Transform cell;
  141.                 cell = Grid[x,z];
  142.                 //Create a new CellScript variable to
  143.                 // hold the cell's script component.
  144.                 CellScript cScript = cell.GetComponent<CellScript>();
  145.                 //As long as it is valid,
  146.                 // Add the left, right, down and up cells adjacent
  147.                 // to the list inside this cell's cellScript.
  148.                 if(x - 1 >= 0+1){
  149.                     cScript.Adjacents.Add(Grid[x-1,z]);
  150.                 }
  151.                 if(x + 1 < Size.x-1){
  152.                     cScript.Adjacents.Add(Grid[x+1,z]);
  153.                 }
  154.                 if(z - 1 >= 0+1){
  155.                     cScript.Adjacents.Add(Grid[x,z-1]);
  156.                 }
  157.                 if(z + 1 < Size.z-1){
  158.                     cScript.Adjacents.Add(Grid[x,z+1]);
  159.                 }
  160.                 //After each cell has been validated and entered,
  161.                 // sort all the adjacents in the list
  162.                 // by the lowest weight.
  163.                 cScript.Adjacents.Sort(SortByLowestWeight);
  164.             }
  165.         }
  166.     }
  167.    
  168.     //Check the link for more info on custom comparers and sorts.
  169.     //http://msdn.microsoft.com/en-us/library/0e743hdt.aspx
  170.     int SortByLowestWeight(Transform inputA, Transform inputB){
  171.         //Given two transforms, find which cellScript's Weight
  172.         // is the highest. Sort by the Weights.
  173.         int a = inputA.GetComponent<CellScript>().Weight; //a's weight
  174.         int b = inputB.GetComponent<CellScript>().Weight; //b's weight
  175.         return a.CompareTo(b);
  176.     }
  177.    
  178.     //This list is used for Prim's Algorithm.
  179.     // We start with an empty list,
  180.     // but as the maze is created, cells will be
  181.     // continuously added to this Set list.
  182.     // Look at the Wikipedia page for more info
  183.     // on Prim's.
  184.     // http://en.wikipedia.org/wiki/Prim%27s_algorithm
  185.     public List<Transform> Set;
  186.    
  187.     //Here we have a List of Lists.
  188.     // Here is the structure:
  189.     //  AdjSet{
  190.     //       [ 0 ] is a list of all the cells
  191.     //         that have a weight of 0, and are
  192.     //         adjacent to the cells in Set.
  193.     //       [ 1 ] is a list of all the cells
  194.     //         that have a weight of 1, and are
  195.     //         adjacent to the cells in Set.
  196.     //       [ 2 ] is a list of all the cells
  197.     //         that have a weight of 2, and are
  198.     //         adjacent to the cells in Set.
  199.     //     etc...
  200.     //       [ 9 ] is a list of all the cells
  201.     //         that have a weight of 9, and are
  202.     //         adjacent to the cells in Set.
  203.     //  }
  204.     //
  205.     // Note: Multiple entries of the same cell
  206.     //  will not appear as duplicates.
  207.     //  (Some adjacent cells will be next to
  208.     //  two or three or four other Set cells).
  209.     //  They are only recorded in the AdjSet once.
  210.     public List<List<Transform>> AdjSet;
  211.    
  212.     void SetStart(){
  213.         //Create a new List<Transform> for Set.
  214.         Set = new List<Transform>();
  215.         //Also, we create a new List<List<Transform>>
  216.         // and in the For loop, List<Transform>'s.
  217.         AdjSet = new List<List<Transform>>();
  218.         for(int i = 0; i < 10; i++){
  219.             AdjSet.Add(new List<Transform>()); 
  220.         }
  221.         //The Start of our Maze/Set will be color
  222.         // coded Green, so we apply that to the renderer's
  223.         // material's color here.
  224.         Grid[0,0].renderer.material.color = Color.green;
  225.         //Now, we add the first cell to the Set.
  226.         AddToSet(Grid[0,0]);
  227.     }
  228.    
  229.     void AddToSet(Transform toAdd){
  230.         //Adds the toAdd object to the Set.
  231.         // The toAdd transform is sent as a parameter.
  232.         Set.Add(toAdd);
  233.         //For every adjacent next to the toAdd object:
  234.         foreach(Transform adj in toAdd.GetComponent<CellScript>().Adjacents){
  235.             //Add one to the adjacent's cellScript's AdjacentsOpened
  236.             adj.GetComponent<CellScript>().AdjacentsOpened++;
  237.             //If
  238.             // a) The Set does not contain the adjacent
  239.             //   (cells in the Set are not valid to be entered as
  240.             //   adjacentCells as well).
  241.             //  and
  242.             // b) The AdjSet does not already contain the adjacent cell.
  243.             // then..
  244.             if(!Set.Contains(adj) && !(AdjSet[adj.GetComponent<CellScript>().Weight].Contains(adj))){
  245.                 //.. Add this new cell into the proper AdjSet sub-list.
  246.                 AdjSet[adj.GetComponent<CellScript>().Weight].Add(adj);
  247.             }
  248.         }
  249.     }
  250.    
  251.     void FindNext(){
  252.         //We create an empty Transform variable
  253.         // to store the next cell in.
  254.         Transform next;
  255.         //Perform this loop
  256.         // While:
  257.         //  The proposed next gameObject's AdjacentsOpened
  258.         //   is less than or equal to 2.
  259.         //   This is to ensure the maze-like structure.
  260.         do{
  261.             //We'll initially assume that each sub-list of AdjSet is empty
  262.             // and try to prove that assumption false in the for loop.
  263.             // This boolean value will keep track.
  264.             bool empty = true;
  265.             //We'll also take a note of which list is the Lowest,
  266.             // and store it in this variable.
  267.             int lowestList = 0;
  268.             for(int i = 0; i < 10; i++){
  269.                 //We loop through each sub-list in the AdjSet list of
  270.                 // lists, until we find one with a count of more than 0.
  271.                 // If there are more than 0 items in the sub-list,
  272.                 // it is not empty.
  273.                 //We then stop the loop by using the break keyword;
  274.                 // We've found the lowest sub-list, so there is no need
  275.                 // to continue searching.
  276.                 lowestList = i;
  277.                 if(AdjSet[i].Count > 0){
  278.                     empty = false;
  279.                     break;
  280.                 }
  281.             }
  282.             //There is a chance that none of the sub-lists of AdjSet will
  283.             // have any items in them.
  284.             //If this happens, then we have no more cells to open, and
  285.             // are done with the maze production.
  286.             if(empty){
  287.                 //If we finish, as stated and determined above,
  288.                 // display a message to the DebugConsole
  289.                 // that includes how many seconds it took to finish.
  290.                 Debug.Log("We're Done, "+Time.timeSinceLevelLoad+" seconds taken");
  291.                 //Then, cancel our recursive invokes of the FindNext function,
  292.                 // as we're done with the maze.
  293.                 //If we allowed the invokes to keep going, we will receive an error.
  294.                 CancelInvoke("FindNext");
  295.                 //Set.Count-1 is the index of the last element in Set,
  296.                 // or the last cell we opened.
  297.                 //This will be marked as the end of our maze, and so
  298.                 // we mark it red.
  299.                 Set[Set.Count-1].renderer.material.color = Color.red;
  300.                 //Here's an extra something I put in myself.
  301.                 //Every cell in the grid that is not in the set
  302.                 // will be moved one unit up and turned black.
  303.                 // (I changed the default color from black to clear earlier).
  304.                 // If you instantiate a FirstPersonController in the maze now,
  305.                 // you can actually try walking through it.
  306.                 // It's really hard.
  307.                 foreach(Transform cell in Grid){
  308.                     if(!Set.Contains(cell)){
  309.                         cell.Translate(Vector3.up);
  310.                         cell.renderer.material.color = Color.black;
  311.                     }
  312.                 }
  313.                 return;
  314.             }
  315.         }while(next.GetComponent<CellScript>().AdjacentsOpened >= 2);
  316.         //If we did not finish, then:
  317.         // 1. Use the smallest sub-list in AdjSet
  318.         //     as found earlier with the lowestList
  319.         //     variable.
  320.         // 2. With that smallest sub-list, take the first
  321.         //     element in that list, and use it as the 'next'.
  322.         next = AdjSet[lowestList][0];
  323.         //Since we do not want the same cell in both AdjSet and Set,
  324.         // remove this 'next' variable from AdjSet.
  325.         AdjSet[lowestList].Remove(next);
  326.         //The 'next' transform's material color becomes white.
  327.         next.renderer.material.color = Color.white;
  328.         //We add this 'next' transform to the Set our function.
  329.         AddToSet(next);
  330.         //Recursively call this function as soon as this function
  331.         // finishes.
  332.         Invoke("FindNext", 0);
  333.     }
  334.    
  335.    
  336.     void Update(){
  337.         //When we press the F1 key,
  338.         // we're gonna restart the level.
  339.         // This is for testing purposes.
  340.         if(Input.GetKeyDown(KeyCode.F1)){
  341.             Application.LoadLevel(0);  
  342.         }
  343.     }
  344. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement