PageRenderTime 59ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/Assets/Grid Scripts/GridCreator.cs

https://gitlab.com/spost/eecs290fps
C# | 347 lines | 218 code | 32 blank | 97 comment | 34 complexity | f9d27e85a4e11258357490afd39820f2 MD5 | raw file
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. /**
  5. * Creates a grid of specified dimensions and generates a procedural maze using a
  6. * modified form of Prim's Algorithm.
  7. * @author Timothy Sesler
  8. * @author tds45
  9. * @date 4 February 2014
  10. *
  11. * Adapted from work provided online by Austin Takechi
  12. * Contact: MinoruTono@Gmail.com
  13. */
  14. public class GridCreator : MonoBehaviour {
  15. // textures for the maze itself
  16. public Material floorTexture;
  17. public Material wallTexture;
  18. public Material endTexture;
  19. public Material startTexture;
  20. public Transform CellPrefab;
  21. public Transform WallPrefab;
  22. public Vector3 Size;
  23. public Transform[,] Grid;
  24. public Transform end;
  25. public Transform player;
  26. // prefabs for the pickups
  27. public Transform AmmoPickup;
  28. public Transform BatteryPickup;
  29. public Transform HPPickup;
  30. public Transform OxyPickup;
  31. public static float dimensions;
  32. // Monster prefabs
  33. public Transform MonsterPrefab;
  34. public int Monsters = 0;
  35. public int MonstersSpawned = 0;
  36. // holders for the walls that surround the maze and keep the player in
  37. private Transform wall1;
  38. private Transform wall2;
  39. private Transform wall3;
  40. private Transform wall4;
  41. private List<Transform> allPickups;
  42. // Use this for initialization
  43. void Start () {
  44. if (Monsters < 1) {
  45. Monsters = 1;
  46. }
  47. else {
  48. Monsters = Monsters * 4;
  49. }
  50. MonstersSpawned = 0;
  51. CreateGrid();
  52. SetRandomNumbers();
  53. SetAdjacents();
  54. SetStart();
  55. FindNext();
  56. BuildWalls();
  57. allPickups = new List<Transform>();
  58. }
  59. // Creates the grid by instantiating provided cell prefabs.
  60. void CreateGrid () {
  61. Grid = new Transform[(int)Size.x,(int)Size.z];
  62. dimensions = (Size.x - 1) * 6;
  63. // Places the cells and names them according to their coordinates in the grid.
  64. for (int x = 0; x < Size.x; x++) {
  65. for (int z = 0; z < Size.z; z++) {
  66. Transform newCell;
  67. newCell = (Transform)Instantiate(CellPrefab, new Vector3(6 * x, 0, 6 * z), Quaternion.identity);
  68. newCell.name = string.Format("({0},0,{1})", x, z);
  69. newCell.parent = transform;
  70. newCell.GetComponent<CellScript>().Position = new Vector3(6 * x, 0, 6 * z);
  71. Grid[x,z] = newCell;
  72. }
  73. }
  74. // Centers the camera on the maze.
  75. // Feel free to adjust this as needed.
  76. //Camera.main.transform.position = Grid[(int)(Size.x / 2f),(int)(Size.z / 2f)].position + Vector3.up * 100f;
  77. //Camera.main.orthographicSize = Mathf.Max(Size.x * 0.55f, Size.z * 0.5f);
  78. }
  79. // Sets a random weight to each cell.
  80. void SetRandomNumbers () {
  81. foreach (Transform child in transform) {
  82. int weight = Random.Range(0,10);
  83. child.GetComponentInChildren<TextMesh>().text = weight.ToString();
  84. child.GetComponent<CellScript>().Weight = weight;
  85. }
  86. }
  87. // Determines the adjacent cells of each cell in the grid.
  88. void SetAdjacents () {
  89. for(int x = 0; x < Size.x; x++){
  90. for (int z = 0; z < Size.z; z++) {
  91. Transform cell;
  92. cell = Grid[x,z];
  93. CellScript cScript = cell.GetComponent<CellScript>();
  94. if (x - 1 >= 0) {
  95. cScript.Adjacents.Add(Grid[x - 1, z]);
  96. }
  97. if (x + 1 < Size.x) {
  98. cScript.Adjacents.Add(Grid[x + 1, z]);
  99. }
  100. if (z - 1 >= 0) {
  101. cScript.Adjacents.Add(Grid[x, z - 1]);
  102. }
  103. if (z + 1 < Size.z) {
  104. cScript.Adjacents.Add(Grid[x, z + 1]);
  105. }
  106. cScript.Adjacents.Sort(SortByLowestWeight);
  107. }
  108. }
  109. }
  110. // Sorts the weights of adjacent cells.
  111. // Check the link for more info on custom comparators and sorting.
  112. // http://msdn.microsoft.com/en-us/library/0e743hdt.aspx
  113. int SortByLowestWeight (Transform inputA, Transform inputB) {
  114. int a = inputA.GetComponent<CellScript>().Weight;
  115. int b = inputB.GetComponent<CellScript>().Weight;
  116. return a.CompareTo(b);
  117. }
  118. /*********************************************************************
  119. * Everything after this point pertains to generating the actual maze.
  120. * Look at the Wikipedia page for more info on Prim's Algorithm.
  121. * http://en.wikipedia.org/wiki/Prim%27s_algorithm
  122. ********************************************************************/
  123. public List<Transform> PathCells; // The cells in the path through the grid.
  124. public List<List<Transform>> AdjSet; // A list of lists representing available adjacent cells.
  125. /** Here is the structure:
  126. * AdjSet{
  127. * [ 0 ] is a list of all the cells
  128. * that have a weight of 0, and are
  129. * adjacent to the cells in the path
  130. * [ 1 ] is a list of all the cells
  131. * that have a weight of 1, and are
  132. * adjacent to the cells in the path
  133. * ...
  134. * [ 9 ] is a list of all the cells
  135. * that have a weight of 9, and are
  136. * adjacent to the cells in the path
  137. * }
  138. *
  139. * Note: Multiple entries of the same cell
  140. * will not appear as duplicates.
  141. * (Some adjacent cells will be next to
  142. * two or three or four other path cells).
  143. * They are only recorded in the AdjSet once.
  144. */
  145. // Initializes the sets and the starting cell.
  146. void SetStart () {
  147. PathCells = new List<Transform>();
  148. AdjSet = new List<List<Transform>>();
  149. for (int i = 0; i < 10; i++) {
  150. AdjSet.Add(new List<Transform>());
  151. }
  152. AddToSet(Grid[0, 0]);
  153. }
  154. // Adds a cell to the set of visited cells.
  155. void AddToSet (Transform cellToAdd) {
  156. PathCells.Add(cellToAdd);
  157. foreach (Transform adj in cellToAdd.GetComponent<CellScript>().Adjacents) {
  158. adj.GetComponent<CellScript>().AdjacentsOpened++;
  159. if (!PathCells.Contains(adj) && !(AdjSet[adj.GetComponent<CellScript>().Weight].Contains(adj))) {
  160. AdjSet[adj.GetComponent<CellScript>().Weight].Add(adj);
  161. }
  162. }
  163. }
  164. // Determines the next cell to be visited.
  165. void FindNext () {
  166. Transform next;
  167. do {
  168. bool isEmpty = true;
  169. int lowestList = 0;
  170. // We loop through each sub-list in the AdjSet list of lists, until we find one with a count of more than 0.
  171. // If there are more than 0 items in the sub-list, it is not empty.
  172. // We've found the lowest sub-list, so there is no need to continue searching.
  173. for (int i = 0; i < 10; i++) {
  174. lowestList = i;
  175. if (AdjSet[i].Count > 0) {
  176. isEmpty = false;
  177. break;
  178. }
  179. }
  180. // The maze is complete.
  181. if (isEmpty) {
  182. //Debug.Log("Generation completed in " + Time.timeSinceLevelLoad + " seconds.");
  183. CancelInvoke("FindNext");
  184. end = PathCells[PathCells.Count - 1];
  185. foreach (Transform cell in Grid) {
  186. // Removes displayed weight
  187. cell.GetComponentInChildren<TextMesh>().renderer.enabled = false;
  188. // If the cell we're looking at is a wall...
  189. if (!PathCells.Contains(cell)) {
  190. // ...Make the maze 3D...
  191. cell.localScale += new Vector3(0f, 5f, 0f);
  192. cell.localPosition += new Vector3(0f, 3.5f, 0f);
  193. // ...And give it the wall texture.
  194. cell.renderer.material = wallTexture;
  195. }
  196. // And if it's a floor cell...
  197. else {
  198. // ...Spawn pickups on it...
  199. if (cell != Grid[0,0] && cell != end){
  200. SpawnPickups(cell);
  201. }
  202. // ...Give it the floor texture...
  203. cell.renderer.material = floorTexture;
  204. // And spawn monsters on it if we're not already capped.
  205. if(MonstersSpawned < Monsters){
  206. Instantiate(MonsterPrefab, new Vector3(Random.Range (0, dimensions), 7f, Random.Range (0, dimensions)), Quaternion.identity);
  207. MonstersSpawned++;
  208. }
  209. }
  210. }
  211. // Give the start and exit special textures
  212. Grid[0, 0].renderer.material = startTexture;
  213. end.renderer.material = endTexture;
  214. return;
  215. }
  216. // If we did not finish, then:
  217. // 1. Use the smallest sub-list in AdjSet as found earlier with the lowestList variable.
  218. // 2. With that smallest sub-list, take the first element in that list, and use it as the 'next'.
  219. next = AdjSet[lowestList][0];
  220. // Since we do not want the same cell in both AdjSet and Set, remove this 'next' variable from AdjSet.
  221. AdjSet[lowestList].Remove(next);
  222. } while (next.GetComponent<CellScript>().AdjacentsOpened >= 2); // This keeps the walls in the grid, otherwise Prim's Algorithm would just visit every cell
  223. // The 'next' transform's material color becomes white.
  224. next.renderer.material.color = Color.white;
  225. // We add this 'next' transform to the Set our function.
  226. AddToSet(next);
  227. // Recursively call this function as soon as it finishes.
  228. Invoke("FindNext", 0);
  229. }
  230. /*
  231. * Randomly spawns pickups on a given cell. 15% chance to spawn one of 4
  232. * pickups, chosen randomly.
  233. * @param cell The cell to spawn the pickup on
  234. */
  235. void SpawnPickups(Transform cell){
  236. if (Random.Range(0, 100) <= 15){
  237. Transform pickup = null;
  238. switch(Random.Range(0, 4)){
  239. case 0:
  240. pickup = Instantiate(AmmoPickup, cell.localPosition + Vector3.up, Quaternion.identity) as Transform;
  241. break;
  242. case 1:
  243. pickup = Instantiate(HPPickup, cell.localPosition + Vector3.up, Quaternion.identity) as Transform;
  244. break;
  245. case 2:
  246. pickup = Instantiate(BatteryPickup, cell.localPosition + Vector3.up, Quaternion.identity) as Transform;
  247. break;
  248. case 3:
  249. pickup = Instantiate(OxyPickup, cell.localPosition + Vector3.up, Quaternion.identity) as Transform;
  250. break;
  251. default:
  252. Debug.Log("Bad spawn attempt");
  253. break;
  254. }
  255. if(pickup != null){
  256. allPickups.Add(pickup);
  257. }
  258. }
  259. }
  260. /*
  261. * Creates walls surrouncing the maze. No arguments, no return value.
  262. */
  263. void BuildWalls() {
  264. //Wall 1
  265. wall1 = (Transform)Instantiate(WallPrefab, new Vector3(-4.5f, 3.5f, (Size.z / 2f) * 6f - 3f), Quaternion.identity);
  266. wall1.name = string.Format("Wall");
  267. wall1.localScale += new Vector3(2f, 5f, Size.z * 6f);
  268. wall1.renderer.material.mainTextureScale = new Vector2(Size.z,1);
  269. //Wall 2
  270. wall2 = (Transform)Instantiate(WallPrefab, new Vector3(6f * Size.x - 1.5f, 3.5f, (Size.z / 2f) * 6f - 3f), Quaternion.identity);
  271. wall2.name = string.Format("Wall");
  272. wall2.localScale += new Vector3(2f, 5f, Size.z * 6f);
  273. wall2.renderer.material.mainTextureScale = new Vector2(Size.z,1);
  274. //Wall 3
  275. wall3 = (Transform)Instantiate(WallPrefab, new Vector3((Size.x / 2f) * 6f - 3f, 3.5f, -4.5f), Quaternion.identity);
  276. wall3.name = string.Format("Wall");
  277. wall3.localScale += new Vector3(Size.x * 6f, 5f, 2f);
  278. wall3.renderer.material.mainTextureScale = new Vector2(Size.z,1);
  279. //Wall 4
  280. wall4 = (Transform)Instantiate(WallPrefab, new Vector3((Size.x / 2f) * 6f - 3f, 3.5f, 6f * Size.z - 1.5f), Quaternion.identity);
  281. wall4.name = string.Format("Wall");
  282. wall4.localScale += new Vector3(Size.x * 6f, 5f, 2f);
  283. wall4.renderer.material.mainTextureScale = new Vector2(Size.z,1);
  284. }
  285. // Called once per frame.
  286. void Update() {
  287. // Check if the player is at the end
  288. if (end != null) {
  289. if ((player.localPosition.x >= end.localPosition.x - 2f)
  290. && (player.localPosition.x <= end.localPosition.x + 2f)
  291. && (player.localPosition.z >= end.localPosition.z - 2f)
  292. && (player.localPosition.z <= end.localPosition.z + 2f)) {
  293. // If so, destroy the old maze...
  294. Destroy(wall1.transform.gameObject);
  295. Destroy(wall2.transform.gameObject);
  296. Destroy(wall3.transform.gameObject);
  297. Destroy(wall4.transform.gameObject);
  298. for (int i = 0; i < transform.childCount; i++) {
  299. Destroy(transform.GetChild(i).gameObject);
  300. }
  301. // ...Remove all the pickups [Broken]
  302. /** foreach (Transform pickup in allPickups) {
  303. if (pickup.gameObject != null){
  304. Destroy(pickup.gameObject);
  305. }
  306. }
  307. */ allPickups.Clear();
  308. // And restart the maze
  309. player.localPosition = new Vector3(0f, 5f, 0f);
  310. Size.Set(Size.x + 5f, Size.y, Size.z + 5f);
  311. Start();
  312. }
  313. }
  314. }
  315. }