public void SpawnFinalDoor(Map map, RectIntExclusive area) { var spawnArea = new RectIntExclusive(); spawnArea.xMin = 0; spawnArea.xMax = area.xMax; spawnArea.yMin = area.yMax - 1; spawnArea.yMax = area.yMax; var wallTiles = map.GetTilesOfType("Wall", spawnArea); for (int i = wallTiles.Count - 1; i >= 0; i--) { var tile = wallTiles[i]; var adjacentTile = Map.instance.tileObjects[tile.y - 1][tile.x]; if (!adjacentTile.ContainsObjectOfType("Floor") || adjacentTile.ContainsObjectOfType("Wall")) { wallTiles.RemoveAt(i); } } int r = UnityEngine.Random.Range(0, wallTiles.Count); var tileToSpawnDoorOn = wallTiles[r]; for (var node = tileToSpawnDoorOn.objectList.First; node != null;) { var next = node.Next; if (node.Value.objectName == "Wall") { tileToSpawnDoorOn.RemoveObject(node.Value, true); } node = next; } tileToSpawnDoorOn.SpawnAndAddObject(finalDoorPrefab); }
public static void DrawRect(Map map, RectIntExclusive area, Color color, Vector2 offset) { Vector2 lowerLeft = new Vector2(area.xMin + offset.x, area.yMin + offset.y); Vector2 lowerRight = new Vector2(area.xMax + 1 - offset.x, area.yMin + offset.y); Vector2 upperLeft = new Vector2(area.xMin + offset.x, area.yMax + 1 - offset.y); Vector2 upperRight = new Vector2(area.xMax + 1 - offset.x, area.yMax + 1 - offset.y); lowerLeft.x *= map.tileWidth; lowerLeft.y *= map.tileHeight; lowerRight.x *= map.tileWidth; lowerRight.y *= map.tileHeight; upperLeft.x *= map.tileWidth; upperLeft.y *= map.tileHeight; upperRight.x *= map.tileWidth; upperRight.y *= map.tileHeight; lowerLeft += (Vector2)map.transform.position; lowerRight += (Vector2)map.transform.position; upperLeft += (Vector2)map.transform.position; upperRight += (Vector2)map.transform.position; Debug.DrawLine(lowerLeft, lowerRight, color); Debug.DrawLine(lowerRight, upperRight, color); Debug.DrawLine(upperRight, upperLeft, color); Debug.DrawLine(upperLeft, lowerLeft, color); }
public void UpdateTiles(Map map, RectIntExclusive area) { for (int y = area.yMin; y <= area.yMax; y++) { for (int x = area.xMin; x <= area.xMax; x++) { AddTileObjects(map, x, y); } } }
public void ForEachTile(RectIntExclusive area, Action <Tile> action) { for (int y = area.yMax; y >= area.yMin; y--) { for (int x = area.xMax; x >= area.xMin; x--) { var tile = tileObjects[y][x]; action(tile); } } }
// Add a floor tile that is part of a straight path between two rooms. // This works for vertical or horizontal paths by allowing the coordinates to be inverted // Returns true signify that the path should end if the tile is already a floor tile, // or if either neighbor perpendicular to the path is a floor tile bool AddStraightPathTile(int i, int j, RectIntExclusive area, bool invert) { bool isFloor = GetTile(i, j, invert) == TileType.FLOOR; bool isLesserNeighborFloor = i - 1 > area.Min(!invert) && GetTile(i - 1, j, invert) == TileType.FLOOR; bool isGreaterNeighborFloor = i + 1 < area.Max(!invert) && GetTile(i + 1, j, invert) == TileType.FLOOR; bool isLesserLesserNeighborFloor = i - 1 - 1 > area.Min(!invert) && GetTile(i - 1 - 1, j, invert) == TileType.FLOOR; bool isGreaterGreaterNeighborFloor = i + 1 + 1 < area.Max(!invert) && GetTile(i + 1 + 1, j, invert) == TileType.FLOOR; SetTile(i, j, TileType.FLOOR, invert); return(isFloor || isLesserNeighborFloor || isGreaterNeighborFloor || isLesserLesserNeighborFloor || isGreaterGreaterNeighborFloor); }
void GenerateRooms(Map map, RectIntExclusive area, int numRooms) { for (int n = 0; n < numRooms; n++) { int w = Random.Range(3, map.width / 3); int h = Random.Range(3, map.height / 3); int x = Random.Range(0, map.width); int y = Random.Range(0, map.height - 2); GenerateRoom(map, x, y, w, h); } }
public void UpdateTiles(RectIntExclusive area) { var map = Map.instance; for (int y = area.yMin; y <= area.yMax; y++) { for (int x = area.xMin; x <= area.xMax; x++) { AddTileObjects(map, x, y); } } }
public List <Tile> GetTilesOfType(string type, RectIntExclusive area) { var tiles = new List <Tile>(); ForEachTile(area, (tile) => { if (tile.ContainsObjectOfType(type)) { tiles.Add(tile); } }); return(tiles); }
// Create a straight (vertical or horizontal) path starting from the edge of an area, moving inwards until // either a floor tile is reached or a the path runs adjacent to a floor tile int CreateStraightPath(int i, int startOfPath, int endOfPath, RectIntExclusive area, bool invert) { int j = startOfPath; int incr = endOfPath > startOfPath ? 1 : -1; for (; j != endOfPath; j += incr) { bool pathEnded = AddStraightPathTile(i, j, area, invert); if (pathEnded) { break; } } return(j); }
// Add a straight (vertical or horizontal) connecting path between two rooms // This works for vertical or horizontal paths by allowing the coordinates to be inverted // Each end of the path terminates when it reaches a floor tile or runs adjacent to a floor tile // A door is placed at each end of the path if appropriate private RectIntExclusive AddStraightConnectingPath(int i, RectIntExclusive areaA, RectIntExclusive areaB, bool invert) { int startOfPath = CreateStraightPath(i, areaA.Max(invert), areaA.Min(invert), areaA, invert); int endOfPath = CreateStraightPath(i, areaB.Min(invert), areaB.Max(invert), areaB, invert); if (IsDoorSpot(i, startOfPath + 1, invert)) { SetTile(i, startOfPath + 1, TileType.DOOR, invert); } if (IsDoorSpot(i - 1, startOfPath, invert)) { SetTile(i - 1, startOfPath, TileType.DOOR, invert); } if (IsDoorSpot(i + 1, startOfPath, invert)) { SetTile(i + 1, startOfPath, TileType.DOOR, invert); } if (IsDoorSpot(i, endOfPath - 1, invert)) { SetTile(i, endOfPath - 1, TileType.DOOR, invert); } if (IsDoorSpot(i - 1, endOfPath, invert)) { SetTile(i - 1, endOfPath, TileType.DOOR, invert); } if (IsDoorSpot(i + 1, endOfPath, invert)) { SetTile(i + 1, endOfPath, TileType.DOOR, invert); } var pathArea = new RectIntExclusive(); if (invert) { pathArea.SetMinMax(startOfPath - 1, endOfPath + 1, i - 1, i + 1); } else { pathArea.SetMinMax(i - 1, i + 1, startOfPath - 1, endOfPath + 1); } return(pathArea); }
public void ForEachTileThatAllowsSpawn(Action <Tile> doThis, RectIntExclusive area) { // If the area is larger than the total number of floor tiles it is more efficient to use the precomputed list if (area.width * area.height > tilesThatAllowSpawn.Count) { ForEachTileThatAllowsSpawn(doThis); return; } for (int y = area.yMax; y >= area.yMin; y--) { for (int x = area.xMax; x >= area.xMin; x--) { var tile = tileObjects[y][x]; if (tile.AllowsSpawn()) { doThis(tile); } } } }
List <Vector2Int> GetLeftFloorTiles(RectIntExclusive area) { List <Vector2Int> floorTiles = new List <Vector2Int>(); for (int y = area.yMin; y <= area.yMax; y++) { for (int x = area.xMin; x <= area.xMax; x++) { if (tiles[y][x] == TileType.FLOOR) { floorTiles.Add(new Vector2Int(x, y)); break; } else if (tiles[y][x] == TileType.DOOR) { break; } } } return(floorTiles); }
override public IEnumerator PreProcessMap(Map map, Biome biome) { tiles = new TileType[map.height][]; for (int y = 0; y < map.height; y++) { tiles[y] = new TileType[map.width]; } var itemsBiome = new Biome(); var itemsBiomeType = Instantiate(items); itemsBiome.biomeType = itemsBiomeType; itemsBiome.area = biome.area; biome.subBiomes.Add(itemsBiome); yield return(new WaitForSeconds(animationDelay * 2)); root = new Node(); root.area = biome.area; yield return(new WaitForSeconds(animationDelay)); yield return(Map.instance.StartCoroutine(GenerateAreas(root, 1))); yield return(Map.instance.StartCoroutine(GenerateRooms(root, biome))); var mapArea = new RectIntExclusive(0, 0, Map.instance.width, Map.instance.height); AddWalls(); if (animationDelay != 0) { UpdateTiles(mapArea); yield return(new WaitForSeconds(animationDelay)); } PruneDoors(); UpdateTiles(mapArea); //SpawnFinalDoor(map, area); }
virtual public void DrawDebug(RectIntExclusive area) { EditorUtil.DrawRect(Map.instance, area, Color.green); }
IEnumerator GenerateRooms(Node parent, Biome biome) { if (parent == null) { yield break; } if (parent.left == null && parent.right == null) { // Leaf node, actually generate a room int areaWidth = parent.area.width - 2; int areaHeight = parent.area.height - 2; int minWidth = Mathf.Max(1, areaWidth / 3); int minHeight = Mathf.Max(1, areaHeight / 3); int w = UnityEngine.Random.Range(minWidth, areaWidth); int h = UnityEngine.Random.Range(minHeight, areaHeight); int xMin = UnityEngine.Random.Range(parent.area.xMin + 1, parent.area.xMax - (w - 1)); int yMin = UnityEngine.Random.Range(parent.area.yMin + 1, parent.area.yMax - (h - 1)); var rect = new RectIntExclusive(xMin, yMin, w, h); parent.room = rect; for (int y = rect.yMin; y <= rect.yMax; y++) { for (int x = rect.xMin; x <= rect.xMax; x++) { tiles[y][x] = TileType.FLOOR; } } var roomBiomeType = Instantiate(scorpions); var roomBiome = new Biome(); roomBiome.biomeType = roomBiomeType; roomBiome.area = rect; biome.subBiomes.Add(roomBiome); for (int y = rect.yMin - 1; y <= rect.yMax + 1; y++) { for (int x = rect.xMin - 1; x <= rect.xMax + 1; x++) { Map.instance.tileObjects[y][Map.instance.WrapX(x)].isAlwaysLit = true; Map.instance.tileObjects[y][Map.instance.WrapX(x)].SetLit(true); } } if (animationDelay != 0) { UpdateTiles(parent.room); yield return(new WaitForSeconds(animationDelay)); } } else { // Not a leaf node, keep traversing the tree yield return(Map.instance.StartCoroutine(GenerateRooms(parent.left, biome))); yield return(Map.instance.StartCoroutine(GenerateRooms(parent.right, biome))); // Ensure at least one path between the two subtrees if (parent.left.area.xMax == parent.right.area.xMax) { // Vertical split // Get a list of floor tiles at maximum y for each x in bottom split, and the min and max x values that have floor tiles List <Vector2Int> topTilesInBottomSplit = GetTopFloorTiles(parent.left.area); // Get a list of floor tiles at minimum y for each x in top split, and the min and max x values that have floor tiles List <Vector2Int> bottomTilesInTopSplit = GetBottomFloorTiles(parent.right.area); // Get the overlap between the two above lists bool isConnected = false; var overlappingTiles = GetOverlap(topTilesInBottomSplit, bottomTilesInTopSplit, true, out isConnected); RectIntExclusive pathArea; if (!isConnected) { if (overlappingTiles.Count != 0) { // Pick a random x position within the overlapping area to add a connecting path int randomIndex = UnityEngine.Random.Range(0, overlappingTiles.Count); int randomX = overlappingTiles[randomIndex].tileA.x; pathArea = AddStraightConnectingPath(randomX, parent.left.area, parent.right.area, false); if (animationDelay != 0) { UpdateTiles(pathArea); } } else { var bottomTileIndex = UnityEngine.Random.Range(0, topTilesInBottomSplit.Count); var topTileIndex = UnityEngine.Random.Range(0, bottomTilesInTopSplit.Count); var bottomTile = topTilesInBottomSplit[bottomTileIndex]; var topTile = bottomTilesInTopSplit[topTileIndex]; AddAngledPath(bottomTile, topTile, true); if (animationDelay != 0) { UpdateTiles(parent.area); } } yield return(new WaitForSeconds(animationDelay)); } } else { // Horizontal split // Get a list of floor tiles at maximum x for each y in left split, and the min and max y values that have floor tiles List <Vector2Int> rightTilesInLeftSplit = GetRightFloorTiles(parent.left.area); // Get a list of floor tiles at minimum x for each y in top split, and the min and max y values that have floor tiles List <Vector2Int> leftTilesInRightSplit = GetLeftFloorTiles(parent.right.area); // Get the overlap between the two above lists bool isConnected = false; var overlappingTiles = GetOverlap(rightTilesInLeftSplit, leftTilesInRightSplit, false, out isConnected); RectIntExclusive pathArea; if (!isConnected) { if (overlappingTiles.Count != 0) { // Pick a random y position within the overlapping area to add a connecting path int randomIndex = UnityEngine.Random.Range(0, overlappingTiles.Count); int randomY = overlappingTiles[randomIndex].tileA.y; pathArea = AddStraightConnectingPath(randomY, parent.left.area, parent.right.area, true); if (animationDelay != 0) { UpdateTiles(pathArea); } } else { var leftTileIndex = UnityEngine.Random.Range(0, rightTilesInLeftSplit.Count); var rightTileIndex = UnityEngine.Random.Range(0, leftTilesInRightSplit.Count); var leftTile = rightTilesInLeftSplit[leftTileIndex]; var rightTile = leftTilesInRightSplit[rightTileIndex]; AddAngledPath(leftTile, rightTile, false); if (animationDelay != 0) { UpdateTiles(parent.area); } } yield return(new WaitForSeconds(animationDelay)); } } } }
override public void DrawDebug(RectIntExclusive area) { base.DrawDebug(area); DrawArea(root); }
public static void DrawRect(Map map, RectIntExclusive area, Color color) { DrawRect(map, area, color, Vector2.zero); }
IEnumerator GenerateAreas(Node parent, float splitProbability) { if (UnityEngine.Random.value > splitProbability) { yield break; } bool horizontalHasRoom = parent.area.width > (minBSPArea * 2); bool verticalHasRoom = parent.area.height > (minBSPArea * 2); if (!horizontalHasRoom && !verticalHasRoom) { yield break; } bool splitHorizontal; if (!verticalHasRoom) { splitHorizontal = true; } else if (!horizontalHasRoom) { splitHorizontal = false; } else { if (parent.area.width > parent.area.height) { splitHorizontal = true; } else if (parent.area.height > parent.area.width) { splitHorizontal = false; } else if (UnityEngine.Random.value > .5f) { splitHorizontal = false; } else { splitHorizontal = true; } } if (splitHorizontal) { int splitX = UnityEngine.Random.Range(parent.area.xMin + minBSPArea, parent.area.xMax - minBSPArea); RectIntExclusive newArea = new RectIntExclusive(); newArea.xMin = parent.area.xMin; newArea.xMax = splitX; newArea.yMin = parent.area.yMin; newArea.yMax = parent.area.yMax; Node child1 = new Node(); child1.area = newArea; child1.parent = parent; parent.left = child1; yield return(new WaitForSeconds(animationDelay)); yield return(Map.instance.StartCoroutine(GenerateAreas(child1, splitProbability * .8f))); newArea = new RectIntExclusive(); newArea.xMin = splitX + 1; newArea.xMax = parent.area.xMax; newArea.yMin = parent.area.yMin; newArea.yMax = parent.area.yMax; Node child2 = new Node(); child2.area = newArea; child2.parent = parent; parent.right = child2; yield return(new WaitForSeconds(animationDelay)); yield return(Map.instance.StartCoroutine(GenerateAreas(child2, splitProbability * .8f))); } else { int splitY = UnityEngine.Random.Range(parent.area.yMin + minBSPArea, parent.area.yMax - minBSPArea); RectIntExclusive newArea = new RectIntExclusive(); newArea.xMin = parent.area.xMin; newArea.xMax = parent.area.xMax; newArea.yMin = parent.area.yMin; newArea.yMax = splitY; Node child1 = new Node(); child1.area = newArea; child1.parent = parent; parent.left = child1; yield return(new WaitForSeconds(animationDelay)); yield return(Map.instance.StartCoroutine(GenerateAreas(child1, splitProbability * .8f))); newArea = new RectIntExclusive(); newArea.xMin = parent.area.xMin; newArea.xMax = parent.area.xMax; newArea.yMin = splitY + 1; newArea.yMax = parent.area.yMax; Node child2 = new Node(); child2.area = newArea; child2.parent = parent; parent.right = child2; yield return(new WaitForSeconds(animationDelay)); yield return(Map.instance.StartCoroutine(GenerateAreas(child2, splitProbability * .8f))); } }