Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- using System.Collections;
- using UnityEngine.Tilemaps;
- public class GenerateTerrain : MonoBehaviour
- {
- public Tilemap tileMap;
- public TileBase baseTile;
- public int width;
- public int height;
- public int seed;
- public bool useRandomSeed;
- public int caveCount;
- public int caveVerticalSize;
- public int caveHorizontalSize;
- public int smoothness = 1;
- public int[,] terrain;
- // Start is called before the first frame update
- void Start()
- {
- //Generate a blank array
- terrain = GenerateArray(width, height);
- //Overwrite array with textured terrain depending on if useRandomSeed is true
- if (useRandomSeed)
- {
- terrain = RandomWalkTopSmoothed(terrain, UnityEngine.Random.Range(0, 1000), smoothness);
- }
- else
- {
- terrain = RandomWalkTopSmoothed(terrain, seed, smoothness);
- }
- //Used to get GenerateMap() function
- GenerateCaves caveSystem = gameObject.GetComponent<GenerateCaves>();
- //Used to set real-world coordinates for placing centers of caves
- int[,] center = new int[1, 2];
- int[,] map;
- //USed to determine offset between cave center and rel-world center
- int xShift;
- int yShift;
- for (int i = 0; i < caveCount; i++)
- {
- //Randomly select center coordinates
- center[0, 0] = Mathf.RoundToInt(Mathf.PerlinNoise(i + 1, seed) * width);
- center[0, 1] = Mathf.RoundToInt(Mathf.PerlinNoise(i + 2, seed) * height);
- //Generate cave coordinates
- map = caveSystem.GenerateMap(""+(seed * i), caveHorizontalSize, caveVerticalSize, UnityEngine.Random.Range(0, 100));
- //Determine offset between centers
- xShift = Mathf.Abs((caveHorizontalSize / 2) - center[0, 0]);
- yShift = Mathf.Abs((caveVerticalSize / 2) - center[0, 1]);
- //Loop through each column in the map
- for (int x = 0; x < caveHorizontalSize; x++)
- {
- //Loop through each row in the map
- for (int y = 0; y < caveVerticalSize; y++)
- {
- //Determine if cave (empty block) is in that position
- if (map[x, y] == 0)
- {
- //Apply offset to that position and destroy tile in terrain array
- terrain[x + xShift, y + yShift] = 0;
- }
- }
- }
- }
- //Set tiles from array onto tilemap
- RenderMap(terrain, tileMap, baseTile);
- }
- // Update is called once per frame
- void Update()
- {
- //Constantly update the tilemap
- UpdateMap(terrain, tileMap);
- }
- //Fucntion used to generate bank arrays
- public static int[,] GenerateArray(int width, int height)
- {
- //Create a multi-dimensional integer array with rows and columns according to the width and height parameters
- int[,] map = new int[width, height];
- //Loop through each column in the array
- for (int x = 0; x < map.GetUpperBound(0); x++)
- {
- //Loop through each row in the column
- for (int y = 0; y < map.GetUpperBound(1); y++)
- {
- //Add a placeholder 1 for each row
- map[x, y] = 1;
- }
- }
- return map;
- }
- //Function used to render the array onto the tilemap
- public static void RenderMap(int[,] map, Tilemap tilemap, TileBase tile)
- {
- //Clear the map (ensures we dont overlap)
- tilemap.ClearAllTiles();
- //Loop through the width of the map
- for (int x = 0; x < map.GetUpperBound(0); x++)
- {
- //Loop through the height of the map
- for (int y = 0; y < map.GetUpperBound(1); y++)
- {
- // 1 = tile, 0 = no tile
- if (map[x, y] == 1)
- {
- //Set the tile to baseTile in that Vector if needed
- tilemap.SetTile(new Vector3Int(x, y, 0), tile);
- }
- }
- }
- }
- //Function which takes in our map and tilemap, setting null tiles where needed
- public static void UpdateMap(int[,] map, Tilemap tilemap)
- {
- //Loop through the width of the map
- for (int x = 0; x < map.GetUpperBound(0); x++)
- {
- //Loop through the height of the map
- for (int y = 0; y < map.GetUpperBound(1); y++)
- {
- //We are only going to update the map, rather than rendering again
- //This is because it uses less resources to update tiles to null
- //As opposed to re-drawing every single tile (and collision data)
- if (map[x, y] == 0)
- {
- //Set the tile to baseTile in that Vector if needed
- tilemap.SetTile(new Vector3Int(x, y, 0), null);
- }
- }
- }
- }
- //Function used to create the texture which will overlay onto the blank array
- public static int[,] RandomWalkTopSmoothed(int[,] map, float seed, int minSectionWidth)
- {
- //Store the initial texture coords for each column into this list
- List<Tuple<int, int>> getTexture = new List<Tuple<int, int>>();
- //Seed our random
- System.Random rand = new System.Random(seed.GetHashCode());
- //Determine the start position
- int lastHeight = UnityEngine.Random.Range(0, map.GetUpperBound(1));
- //Used to determine which direction to go
- int nextMove = 0;
- //Used to keep track of the current sections width
- int sectionWidth = 0;
- //Work through the array width
- for (int x = 0; x <= map.GetUpperBound(0); x++)
- {
- //Determine the next move
- nextMove = rand.Next(2);
- //Only change the height if we have used the current height more than the minimum required section width
- if (nextMove == 0 && lastHeight > 0 && sectionWidth > minSectionWidth)
- {
- lastHeight--;
- sectionWidth = 0;
- }
- else if (nextMove == 1 && lastHeight < map.GetUpperBound(1) && sectionWidth > minSectionWidth)
- {
- lastHeight++;
- sectionWidth = 0;
- }
- //Increment the section width
- sectionWidth++;
- //Add coordinate to texture list
- getTexture.Add(Tuple.Create(x, lastHeight));
- }
- //Create the variable to eventually hold the biggest y variable of the coords-
- //In texture list to act as the height limit when overlaying onto the array
- int biggestY = 0;
- //Sort through each coordinate pair in the textures
- foreach (Tuple<int, int> coord in getTexture)
- {
- //Add new y value if it is bigger than currently stored y value
- if (coord.Item2 > biggestY)
- {
- //Overwrite y vaue
- biggestY = coord.Item2;
- }
- }
- //Determine the coordinate shift between the biggest y value and the height limit
- int yShift = map.GetUpperBound(1) - biggestY;
- //Sort through each coordinate pair in the textures
- foreach (Tuple<int, int> coord in getTexture)
- {
- //Start removing extra tiles above the texture by translating the getTexture's coords to the map's coords
- for (int i = coord.Item2 + yShift; i < map.GetUpperBound(1); i++)
- {
- //Set every tile above the texture to 0, as it is not needed
- map[coord.Item1, i] = 0;
- }
- }
- //Return the modified map
- return map;
- }
- }
- ```
- New script
- ```
- using UnityEngine;
- using System.Collections.Generic;
- using System;
- public class GenerateCaves : MonoBehaviour
- {
- int[,] map;
- public int[,] GenerateMap(string seed, int width, int height, int randomFillPercent, int smoothness = 5)
- {
- if(randomFillPercent > 100 || randomFillPercent < 0)
- {
- Debug.LogError("Random Fill Percent must be inbetween 0 and 100");
- }
- map = new int[width, height];
- RandomFillMap(seed, width, height, randomFillPercent);
- for (int i = 0; i < smoothness; i++)
- {
- SmoothMap(width, height);
- }
- ProcessMap(width, height);
- int borderSize = 1;
- int[,] borderedMap = new int[width + borderSize * 2, height + borderSize * 2];
- for (int x = 0; x < borderedMap.GetLength(0); x++)
- {
- for (int y = 0; y < borderedMap.GetLength(1); y++)
- {
- if (x >= borderSize && x < width + borderSize && y >= borderSize && y < height + borderSize)
- {
- borderedMap[x, y] = map[x - borderSize, y - borderSize];
- }
- else
- {
- borderedMap[x, y] = 1;
- }
- }
- }
- return borderedMap;
- }
- void ProcessMap(int width, int height)
- {
- List<List<Coord>> wallRegions = GetRegions(1, width, height);
- int wallThresholdSize = 50;
- foreach (List<Coord> wallRegion in wallRegions)
- {
- if (wallRegion.Count < wallThresholdSize)
- {
- foreach (Coord tile in wallRegion)
- {
- map[tile.tileX, tile.tileY] = 0;
- }
- }
- }
- List<List<Coord>> roomRegions = GetRegions(0, width, height);
- int roomThresholdSize = 50;
- List<Room> survivingRooms = new List<Room>();
- foreach (List<Coord> roomRegion in roomRegions)
- {
- if (roomRegion.Count < roomThresholdSize)
- {
- foreach (Coord tile in roomRegion)
- {
- map[tile.tileX, tile.tileY] = 1;
- }
- }
- else
- {
- survivingRooms.Add(new Room(roomRegion, map));
- }
- }
- survivingRooms.Sort();
- survivingRooms[0].isMainRoom = true;
- survivingRooms[0].isAccessibleFromMainRoom = true;
- ConnectClosestRooms(survivingRooms, width, height);
- }
- void ConnectClosestRooms(List<Room> allRooms, int width, int height, bool forceAccessibilityFromMainRoom = false)
- {
- List<Room> roomListA = new List<Room>();
- List<Room> roomListB = new List<Room>();
- if (forceAccessibilityFromMainRoom)
- {
- foreach (Room room in allRooms)
- {
- if (room.isAccessibleFromMainRoom)
- {
- roomListB.Add(room);
- }
- else
- {
- roomListA.Add(room);
- }
- }
- }
- else
- {
- roomListA = allRooms;
- roomListB = allRooms;
- }
- int bestDistance = 0;
- Coord bestTileA = new Coord();
- Coord bestTileB = new Coord();
- Room bestRoomA = new Room();
- Room bestRoomB = new Room();
- bool possibleConnectionFound = false;
- foreach (Room roomA in roomListA)
- {
- if (!forceAccessibilityFromMainRoom)
- {
- possibleConnectionFound = false;
- if (roomA.connectedRooms.Count > 0)
- {
- continue;
- }
- }
- foreach (Room roomB in roomListB)
- {
- if (roomA == roomB || roomA.IsConnected(roomB))
- {
- continue;
- }
- for (int tileIndexA = 0; tileIndexA < roomA.edgeTiles.Count; tileIndexA++)
- {
- for (int tileIndexB = 0; tileIndexB < roomB.edgeTiles.Count; tileIndexB++)
- {
- Coord tileA = roomA.edgeTiles[tileIndexA];
- Coord tileB = roomB.edgeTiles[tileIndexB];
- int distanceBetweenRooms = (int)(Mathf.Pow(tileA.tileX - tileB.tileX, 2) + Mathf.Pow(tileA.tileY - tileB.tileY, 2));
- if (distanceBetweenRooms < bestDistance || !possibleConnectionFound)
- {
- bestDistance = distanceBetweenRooms;
- possibleConnectionFound = true;
- bestTileA = tileA;
- bestTileB = tileB;
- bestRoomA = roomA;
- bestRoomB = roomB;
- }
- }
- }
- }
- if (possibleConnectionFound && !forceAccessibilityFromMainRoom)
- {
- CreatePassage(bestRoomA, bestRoomB, bestTileA, bestTileB, width, height);
- }
- }
- if (possibleConnectionFound && forceAccessibilityFromMainRoom)
- {
- CreatePassage(bestRoomA, bestRoomB, bestTileA, bestTileB, width, height);
- ConnectClosestRooms(allRooms, width, height, true);
- }
- if (!forceAccessibilityFromMainRoom)
- {
- ConnectClosestRooms(allRooms, width, height, true);
- }
- }
- void CreatePassage(Room roomA, Room roomB, Coord tileA, Coord tileB, int width, int height)
- {
- Room.ConnectRooms(roomA, roomB);
- //Debug.DrawLine (CoordToWorldPoint (tileA), CoordToWorldPoint (tileB), Color.green, 100);
- List<Coord> line = GetLine(tileA, tileB);
- foreach (Coord c in line)
- {
- DrawCircle(c, 5, width, height);
- }
- }
- void DrawCircle(Coord c, int r, int width, int height)
- {
- for (int x = -r; x <= r; x++)
- {
- for (int y = -r; y <= r; y++)
- {
- if (x * x + y * y <= r * r)
- {
- int drawX = c.tileX + x;
- int drawY = c.tileY + y;
- if (IsInMapRange(drawX, drawY, width, height))
- {
- map[drawX, drawY] = 0;
- }
- }
- }
- }
- }
- List<Coord> GetLine(Coord from, Coord to)
- {
- List<Coord> line = new List<Coord>();
- int x = from.tileX;
- int y = from.tileY;
- int dx = to.tileX - from.tileX;
- int dy = to.tileY - from.tileY;
- bool inverted = false;
- int step = Math.Sign(dx);
- int gradientStep = Math.Sign(dy);
- int longest = Mathf.Abs(dx);
- int shortest = Mathf.Abs(dy);
- if (longest < shortest)
- {
- inverted = true;
- longest = Mathf.Abs(dy);
- shortest = Mathf.Abs(dx);
- step = Math.Sign(dy);
- gradientStep = Math.Sign(dx);
- }
- int gradientAccumulation = longest / 2;
- for (int i = 0; i < longest; i++)
- {
- line.Add(new Coord(x, y));
- if (inverted)
- {
- y += step;
- }
- else
- {
- x += step;
- }
- gradientAccumulation += shortest;
- if (gradientAccumulation >= longest)
- {
- if (inverted)
- {
- x += gradientStep;
- }
- else
- {
- y += gradientStep;
- }
- gradientAccumulation -= longest;
- }
- }
- return line;
- }
- List<List<Coord>> GetRegions(int tileType, int width, int height)
- {
- List<List<Coord>> regions = new List<List<Coord>>();
- int[,] mapFlags = new int[width, height];
- for (int x = 0; x < width; x++)
- {
- for (int y = 0; y < height; y++)
- {
- if (mapFlags[x, y] == 0 && map[x, y] == tileType)
- {
- List<Coord> newRegion = GetRegionTiles(x, y, width, height);
- regions.Add(newRegion);
- foreach (Coord tile in newRegion)
- {
- mapFlags[tile.tileX, tile.tileY] = 1;
- }
- }
- }
- }
- return regions;
- }
- List<Coord> GetRegionTiles(int startX, int startY, int width, int height)
- {
- List<Coord> tiles = new List<Coord>();
- int[,] mapFlags = new int[width, height];
- int tileType = map[startX, startY];
- Queue<Coord> queue = new Queue<Coord>();
- queue.Enqueue(new Coord(startX, startY));
- mapFlags[startX, startY] = 1;
- while (queue.Count > 0)
- {
- Coord tile = queue.Dequeue();
- tiles.Add(tile);
- for (int x = tile.tileX - 1; x <= tile.tileX + 1; x++)
- {
- for (int y = tile.tileY - 1; y <= tile.tileY + 1; y++)
- {
- if (IsInMapRange(x, y, width, height) && (y == tile.tileY || x == tile.tileX))
- {
- if (mapFlags[x, y] == 0 && map[x, y] == tileType)
- {
- mapFlags[x, y] = 1;
- queue.Enqueue(new Coord(x, y));
- }
- }
- }
- }
- }
- return tiles;
- }
- bool IsInMapRange(int x, int y, int width, int height)
- {
- return x >= 0 && x < width && y >= 0 && y < height;
- }
- void RandomFillMap(string seed, int width, int height, int randomFillPercent)
- {
- System.Random pseudoRandom = new System.Random(seed.GetHashCode());
- for (int x = 0; x < width; x++)
- {
- for (int y = 0; y < height; y++)
- {
- if (x == 0 || x == width - 1 || y == 0 || y == height - 1)
- {
- map[x, y] = 1;
- }
- else
- {
- map[x, y] = (pseudoRandom.Next(0, 100) < randomFillPercent) ? 1 : 0;
- }
- }
- }
- }
- void SmoothMap(int width, int height)
- {
- for (int x = 0; x < width; x++)
- {
- for (int y = 0; y < height; y++)
- {
- int neighbourWallTiles = GetSurroundingWallCount(x, y, width, height);
- if (neighbourWallTiles > 4)
- map[x, y] = 1;
- else if (neighbourWallTiles < 4)
- map[x, y] = 0;
- }
- }
- }
- int GetSurroundingWallCount(int gridX, int gridY, int width, int height)
- {
- int wallCount = 0;
- for (int neighbourX = gridX - 1; neighbourX <= gridX + 1; neighbourX++)
- {
- for (int neighbourY = gridY - 1; neighbourY <= gridY + 1; neighbourY++)
- {
- if (IsInMapRange(neighbourX, neighbourY, width, height))
- {
- if (neighbourX != gridX || neighbourY != gridY)
- {
- wallCount += map[neighbourX, neighbourY];
- }
- }
- else
- {
- wallCount++;
- }
- }
- }
- return wallCount;
- }
- struct Coord
- {
- public int tileX;
- public int tileY;
- public Coord(int x, int y)
- {
- tileX = x;
- tileY = y;
- }
- }
- class Room : IComparable<Room>
- {
- public List<Coord> tiles;
- public List<Coord> edgeTiles;
- public List<Room> connectedRooms;
- public int roomSize;
- public bool isAccessibleFromMainRoom;
- public bool isMainRoom;
- public Room()
- {
- }
- public Room(List<Coord> roomTiles, int[,] map)
- {
- tiles = roomTiles;
- roomSize = tiles.Count;
- connectedRooms = new List<Room>();
- edgeTiles = new List<Coord>();
- foreach (Coord tile in tiles)
- {
- for (int x = tile.tileX - 1; x <= tile.tileX + 1; x++)
- {
- for (int y = tile.tileY - 1; y <= tile.tileY + 1; y++)
- {
- if (x == tile.tileX || y == tile.tileY)
- {
- if (map[x, y] == 1)
- {
- edgeTiles.Add(tile);
- }
- }
- }
- }
- }
- }
- public void SetAccessibleFromMainRoom()
- {
- if (!isAccessibleFromMainRoom)
- {
- isAccessibleFromMainRoom = true;
- foreach (Room connectedRoom in connectedRooms)
- {
- connectedRoom.SetAccessibleFromMainRoom();
- }
- }
- }
- public static void ConnectRooms(Room roomA, Room roomB)
- {
- if (roomA.isAccessibleFromMainRoom)
- {
- roomB.SetAccessibleFromMainRoom();
- }
- else if (roomB.isAccessibleFromMainRoom)
- {
- roomA.SetAccessibleFromMainRoom();
- }
- roomA.connectedRooms.Add(roomB);
- roomB.connectedRooms.Add(roomA);
- }
- public bool IsConnected(Room otherRoom)
- {
- return connectedRooms.Contains(otherRoom);
- }
- public int CompareTo(Room otherRoom)
- {
- return otherRoom.roomSize.CompareTo(roomSize);
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement