private IEnumerable <TileAndDepth> iterateTilesNearUnit(Unit unit, int maxDepth) { Vector2 mapPos = map.gameToMap(unit.getPosition()); // TODO: handle out-of-bounds units yield return(new TileAndDepth(mapPos, 0)); RandomSet <Vector2> tilesInfluencedByThisUnit = new RandomSet <Vector2>(); RandomSet <Vector2> previousIterationTiles = new RandomSet <Vector2>(); tilesInfluencedByThisUnit.Add(mapPos); previousIterationTiles.Add(mapPos); for (int iteration = 1; iteration <= maxDepth; iteration++) { RandomSet <Vector2> currentIterationTiles = new RandomSet <Vector2>(); foreach (Vector2 tile in previousIterationTiles) { currentIterationTiles.UnionWith(map.getValidNeighbour4(tile, walkableTiles)); } currentIterationTiles.DifferenceWith(tilesInfluencedByThisUnit); tilesInfluencedByThisUnit.UnionWith(currentIterationTiles); playerInfluencedTiles.UnionWith(currentIterationTiles); foreach (Vector2 tile in currentIterationTiles) { yield return(new TileAndDepth(tile, iteration)); } previousIterationTiles = currentIterationTiles; } }
// Returns neighbours of currentTile that are also present in validTiles. public RandomSet <Vector2> getValidNeighbour8(Vector2 currentTile, ICollection <Vector2> validTiles) { RandomSet <Vector2> potentialNeighbours = new RandomSet <Vector2>(); potentialNeighbours.UnionWith(getNeighbours8(currentTile)); potentialNeighbours.IntersectWith(validTiles); return(potentialNeighbours); }
public void generateMap(List <Player> players, int width = 16, int height = 16, int tilesBetweenIslands = 2) { hasBeenGenerated = true; mapWidth = width; mapHeight = height; tileMap = new List <List <Tile> >(); buildings = new List <Building>(); // Default the entire map to water. RandomSet <Vector2> waterTiles = new RandomSet <Vector2>(); for (int x = 0; x < mapWidth; ++x) { tileMap.Add(new List <Tile>()); for (int y = 0; y < mapHeight; ++y) { tileMap[x].Add(Tile.WATER); // The border of the map is water, but we don't want to spawn islands that touch the edge. if (x > 0 && x < mapWidth - 1 && y > 0 && y < mapHeight - 1) { waterTiles.Add(new Vector2(x, y)); } } } // Terrain int numislands = 12; //15; int islandSizeMin = 3; int islandSizeMax = 18; int treeDensity = 4; int numcolonies = 20; //Random.seed = for (int islandsCreated = 0; islandsCreated < numislands && waterTiles.Count > 0; islandsCreated++) { Vector2 initialTile = waterTiles.popRandom(); // A set of water values that could be made into land RandomSet <Vector2> adjacentWater = new RandomSet <Vector2>(); RandomSet <Vector2> islandTiles = new RandomSet <Vector2>(); adjacentWater.Add(initialTile); int islandSize = Random.Range(islandSizeMin, islandSizeMax + 1); for (int n = 0; n < islandSize && adjacentWater.Count > 0; n++) { Vector2 randTile = adjacentWater.popRandom(); waterTiles.Remove(randTile); adjacentWater.UnionWith(getValidNeighbour4(randTile, waterTiles)); setTile(randTile, Tile.GRASS); GameObject island = Instantiate(islandObject); island.transform.parent = transform; island.transform.localPosition = mapToGame(randTile); islandTiles.Add(randTile); } // Remove water tiles near the island from the available water tiles for future island generation. for (int i = 0; i < tilesBetweenIslands; i++) { RandomSet <Vector2> temporarySet = new RandomSet <Vector2>(); foreach (Vector2 tile in islandTiles) { temporarySet.UnionWith(getValidNeighbour8(tile, waterTiles)); } islandTiles = temporarySet; waterTiles.DifferenceWith(islandTiles); } } // Floodfill water tiles that are reachable from the corner of the map. // For each tile, count the number of adjacent land tiles. // Any water tile with 1-2 adjacent land tiles is a valid place for a dock. // The set of land tiles adjacent to validDockTiles is the set of validBuildingTiles. HashSet <Vector2> seen = new HashSet <Vector2>(); Queue <Vector2> todo = new Queue <Vector2>(); HashSet <Vector2> validDockTiles = new HashSet <Vector2>(); RandomSet <Vector2> validBuildingTiles = new RandomSet <Vector2>(); // since I excluded the outer border, it is definitely water todo.Enqueue(new Vector2(0, 0)); seen.Add(todo.Peek()); while (todo.Count > 0) { Vector2 randTile = todo.Dequeue(); HashSet <Vector2> adjacentCoasts = new HashSet <Vector2>(); foreach (Vector2 adj in getNeighbours4(randTile)) { if (isWater(getTile(adj))) { if (!seen.Contains(adj)) { todo.Enqueue(adj); } } else { adjacentCoasts.Add(adj); } seen.Add(adj); } if (adjacentCoasts.Count > 0 && adjacentCoasts.Count < 3) { validDockTiles.Add(randTile); validBuildingTiles.UnionWith(adjacentCoasts); } } // Put each player's base at the potential tile closest to their side of the map. Player1 should spawn at -y // Player 0 is neutral. Player 1 should spawn at +y. for (int playerIndex = 1; playerIndex < players.Count; playerIndex++) { float angleFromCenter = (Mathf.PI / 2f) + (playerIndex * 2f * Mathf.PI / (players.Count - 1)); Vector2 preferredTile = 0.9f * new Vector2( mapWidth * (0.5f + 0.5f * Mathf.Cos(angleFromCenter)), mapHeight * (0.5f + 0.5f * Mathf.Sin(angleFromCenter))); float minDistFromPreferred = float.MaxValue; Vector2 buildingPos = Vector2.zero; foreach (Vector2 possibleTile in validBuildingTiles) { // TODO: ignore docks that face away from the center of the map. // (The dot product of dockDirection and angleFromCenter should be negative) float distFromPreferred = (possibleTile - preferredTile).sqrMagnitude; if (distFromPreferred < minDistFromPreferred) { minDistFromPreferred = distFromPreferred; buildingPos = possibleTile; } } validBuildingTiles.Remove(buildingPos); Vector2 dockTile = getValidNeighbour4(buildingPos, validDockTiles).popRandom(); setTile(buildingPos, Tile.BUILDING); Building building = Instantiate(baseObject).GetComponent <Building>(); building.init(buildingPos, dockTile, BuildingType.BASE); building.setOwner(players[playerIndex]); buildings.Add(building); } // colonies for (int i = 0; i < numcolonies && validBuildingTiles.Count > 0; i++) { Vector2 buildingPos = validBuildingTiles.popRandom(); Vector2 dockTile = getValidNeighbour4(buildingPos, validDockTiles).popRandom(); setTile(buildingPos, Tile.BUILDING); Building building = Instantiate(buildingObject).GetComponent <Building>(); building.init(buildingPos, dockTile, BuildingType.COLONY); building.setOwner(players[0]); // Neutral buildings.Add(building); } Pathing.updateMap(this); // Spawn trees. for (int x = 0; x < mapWidth; ++x) { for (int y = 0; y < mapHeight; ++y) { if (tileMap[x][y] == Tile.GRASS) { for (int tr = 0; tr < treeDensity; ++tr) { GameObject tree = Instantiate(treeObject); tree.transform.parent = transform; // z = -0.2f puts the tree in front of the sand tree.transform.localPosition = mapToGame(x, y) + new Vector3( Random.Range(-tileSize * 0.45f, tileSize * 0.45f), Random.Range(-tileSize * 0.45f, tileSize * 0.45f), -0.45f); float sz = Random.Range(tileSize * 0.8f, tileSize * 1.2f); // tree radius is about 0.29 by default tree.transform.localScale = new Vector3(sz, sz, 1f); } } } } }