/// <summary> /// Generate map and set tiles to tilemaps. /// </summary> public StartCoordinates Generate() { /* * OVERVIEW: * Start by generating rooms, that are connected by roads. * Then, as roads now are only outside and between rooms, connect them inside rooms. * Next, flatten rooms to a int [,] map, and find different regions there, as those are what we are interested in. * Also, add walls wherever region or road tile meets empty. * Only horizontal walls are shown in game, so only those tiles are added to wallilemap, but add colliders to every wall position. */ // Clear colliderobject Collider2D[] oldColliders = colliderObject.GetComponents <Collider2D> (); for (int i = 0; i < oldColliders.Length; i++) { GameObject.DestroyImmediate(oldColliders [i]); } // Clear all tilemaps groundTilemap.ClearAllTiles(); if (Application.isPlaying) { for (int i = groundTilemap.transform.childCount - 1; i >= 0; i--) { GameObject.Destroy(groundTilemap.transform.GetChild(i).gameObject); } } roadTilemap.ClearAllTiles(); wallTilemap.ClearAllTiles(); debugTilemap.ClearAllTiles(); // Minimum and maximum coordinates INSIDE room Vector2Int[] roomMins = new Vector2Int[roomsNumber]; Vector2Int[] roomMaxs = new Vector2Int[roomsNumber]; Direction[] directions = new Direction [roomsNumber]; Vector2Int[] roadStarts = new Vector2Int[roomsNumber - 1]; Vector2Int[] roadEnds = new Vector2Int[roomsNumber - 1]; List <Vector2Int> diagonalRoadCoordinates = new List <Vector2Int> (); Vector2Int boundsMin = mapSize; Vector2Int boundsMax = Vector2Int.zero; stopwatch.Reset(); stopwatch.Start(); // Generate First Room, start from roughly center Vector2Int startCoord = new Vector2Int(mapSize.x / 2, mapSize.y / 2); GenerateRoom(startCoord, RandomMajorDirection, out roomMins [0], out roomMaxs [0], out startCoord, out directions [0]); boundsMin = Vector2Int.Min(boundsMin, roomMins [0]); boundsMax = Vector2Int.Max(boundsMax, roomMaxs [0]); // Generate roads and other rooms for (int i = 1; i < roomsNumber; i++) { GenerateRoad(startCoord, directions [i - 1], out roadStarts [i - 1], out roadEnds [i - 1], out startCoord); GenerateRoom(startCoord, directions [i - 1], out roomMins [i], out roomMaxs [i], out startCoord, out directions [i]); boundsMin = Vector2Int.Min(boundsMin, roomMins [i]); boundsMax = Vector2Int.Max(boundsMax, roomMaxs [i]); } // What is going on??? why these need to be expanded so much boundsMax += Vector2Int.one * 3; boundsMin -= Vector2Int.one * 3; // This tries to cover all possible tiles at same time. Vector2Int bitmaskMapSize = new Vector2Int(boundsMax.x - boundsMin.x, boundsMax.y - boundsMin.y); Int32[,] bitmaskMap = new Int32[bitmaskMapSize.x, bitmaskMapSize.y]; // walkablemap is passed to pathfinder to create pathfinding grid int[,] walkableMap = new int[bitmaskMapSize.x, bitmaskMapSize.y]; // Add rooms to bitmaskmap for (int i = 0; i < roomsNumber; i++) { for (int h = roomMins [i].y; h <= roomMaxs [i].y; h++) { for (int w = roomMins [i].x; w <= roomMaxs [i].x; w++) { int x = w - boundsMin.x; int y = h - boundsMin.y; if ((bitmaskMap [x, y] & groundBit) != groundBit) { SetGround(bitmaskMap, x, y); walkableMap [x, y] = 1; } } } } // Add roads to bitmaskmap for (int i = 0; i < roomsNumber - 1; i++) { if (i < roomsNumber - 2) { ConnectRoads( ref roadStarts [i], ref roadEnds [i], directions [i], ref roadStarts [i + 1], ref roadEnds [i + 1], directions [i + 1], bitmaskMap, boundsMin ); } for (int h = roadStarts [i].y - 1; h <= roadEnds [i].y + 1; h++) { for (int w = roadStarts [i].x - 1; w <= roadEnds [i].x + 1; w++) { int x = w - boundsMin.x; int y = h - boundsMin.y; // If this actually is road if (h >= roadStarts [i].y && h <= roadEnds [i].y && w >= roadStarts [i].x && w <= roadEnds [i].x) { if ((bitmaskMap [x, y] & roadBit) != roadBit) { // Add also to walkable map walkableMap [w - boundsMin.x, h - boundsMin.y] = 1; SetRoad(bitmaskMap, x, y); } } else if ((bitmaskMap [x, y] & (groundBit | roadBit)) == 0) { bitmaskMap [x, y] |= wallBit; } } } } // Separate Regions List <List <Vector3Int> > regions = new List <List <Vector3Int> > (); Queue <Vector3Int> coordQueue = new Queue <Vector3Int> (); int [,] checkedCoords = new int[bitmaskMapSize.x, bitmaskMapSize.y]; for (int y = 0; y < bitmaskMapSize.y; y++) { for (int x = 0; x < bitmaskMapSize.x; x++) { if ((bitmaskMap [x, y] & groundBit) == groundBit && checkedCoords [x, y] == 0) { coordQueue.Enqueue(new Vector3Int(x, y, 0)); List <Vector3Int> region = new List <Vector3Int> (); regions.Add(region); // Floodfill with horizontal scanlines while (coordQueue.Count > 0) { Vector3Int coord = coordQueue.Dequeue(); // Expand to east int e = coord.x; while ( e < bitmaskMapSize.x && (bitmaskMap [e, coord.y] & groundBit) == groundBit && checkedCoords[e, coord.y] == 0 ) { e++; } // Expand to west int w = coord.x; while ( w > 0 && (bitmaskMap [w, coord.y] & groundBit) == groundBit && checkedCoords[w, coord.y] == 0 ) { w--; } int n = coord.y + 1; int s = coord.y - 1; // East wall, include corners if (e >= bitmaskMapSize.x || (bitmaskMap [e, coord.y] & (groundBit | roadBit)) == 0) { SetWall(bitmaskMap, e, coord.y); } if (e >= bitmaskMapSize.x || n >= bitmaskMapSize.y || (bitmaskMap [e, n] & (groundBit | roadBit)) == 0) { SetWall(bitmaskMap, e, n); } if (e >= bitmaskMapSize.x || s < 0 || (bitmaskMap [e, s] & (groundBit | roadBit)) == 0) { SetWall(bitmaskMap, e, s); } // West wall, include corners if (w < 0 || (bitmaskMap [w, coord.y] & (groundBit | roadBit)) == 0) { SetWall(bitmaskMap, w, coord.y); } if (w < 0 || n >= bitmaskMapSize.y || (bitmaskMap [w, n] & (groundBit | roadBit)) == 0) { SetWall(bitmaskMap, w, n); } if (w < 0 || s < 0 || (bitmaskMap [w, s] & (groundBit | roadBit)) == 0) { SetWall(bitmaskMap, w, s); } for (coord.x = w + 1; coord.x < e; coord.x++) { region.Add(coord); checkedCoords[coord.x, coord.y] = 1; // Here, add north and south coords to queue if room continues, else add wall (exluding corners) if (n < bitmaskMapSize.y && (bitmaskMap [coord.x, n] & groundBit) == groundBit) { coordQueue.Enqueue(coord + Vector3Int.up); } else if (n >= bitmaskMapSize.y || (bitmaskMap [coord.x, n] & (groundBit | roadBit)) == 0) { SetWall(bitmaskMap, coord.x, n); } if (s >= 0 && (bitmaskMap [coord.x, s] & groundBit) == groundBit) { coordQueue.Enqueue(coord + Vector3Int.down); } else if (s < 0 || (bitmaskMap [coord.x, s] & (groundBit | roadBit)) == 0) { SetWall(bitmaskMap, coord.x, s); } } } } } } List <Vector3> enemyCoordinates = new List <Vector3> (); // Set regions Tiles Vector3Int [][] regionsArray = new Vector3Int[regions.Count][]; Tile[][] regionsTiles = new Tile[regions.Count][]; for (int i = 0; i < regions.Count; i++) { regionsArray [i] = regions [i].ToArray(); regionsTiles [i] = new Tile[regionsArray[i].Length]; TileSet tileSet = tileSets.RandomItem(); for (int j = 0; j < regionsTiles [i].Length; j++) { // Add boundsMax as offset, to get varying noise, its not linked to coordinates here any other way and can be replaced bool hazard = Noise.Bool(tileSet.hazardNoiseMethod, regionsArray [i] [j].x + boundsMax.x, regionsArray [i] [j].y + boundsMax.y, tileSet.hazardPercent); // remove all hazards under roads if ((bitmaskMap [regionsArray[i][j].x, regionsArray [i][j].y] & roadBit) == roadBit) { hazard = false; } regionsTiles [i][j] = hazard ? tileSet.Hazard : tileSet.Ground; if (!hazard) { walkableMap [regionsArray [i] [j].x, regionsArray [i] [j].y] = 1; } if (!hazard && RandomUtility.Percent < _enemyPercent) { enemyCoordinates.Add( new Vector3( regionsArray[i][j].x * grid.cellSize.x + cellCenterOffset.x, regionsArray[i][j].y * grid.cellSize.y + cellCenterOffset.y, 0 ) ); } } } stopwatch.Stop(); if (takeTime) { UnityEngine.Debug.Log(stopwatch.Elapsed); } // Set rooms for (int i = 0; i < regionsArray.Length; i++) { groundTilemap.SetTiles(regionsArray [i], regionsTiles [i]); } // Set roads and walls List <Vector3Int> wallCoordinates = new List <Vector3Int> (); for (int y = 0; y < bitmaskMapSize.y; y++) { for (int x = 0; x < bitmaskMapSize.x; x++) { // Add road if ((bitmaskMap [x, y] & roadBit) == roadBit) { roadTilemap.SetTile(new Vector3Int(x, y, 0), roadTile); // Add walls } else if ((bitmaskMap [x, y] & wallBit) == wallBit) { // solid Int32 mapValue = bitmaskMap [x, y] >> groundShift | bitmaskMap [x, y] >> roadShift; if ((mapValue & bottomMid) == bottomMid) { wallTilemap.SetTile(new Vector3Int(x, y, 0), wallTileSet.GetTile(1)); } // transparent if ((mapValue & topMid) == topMid) { wallTilemap.SetTile(new Vector3Int(x, y + 1, 0), wallTileSet.GetTile(0)); } // Add colliders BoxCollider2D collider = colliderObject.AddComponent <BoxCollider2D> (); collider.offset = (new Vector2(x, y)) * 2 + Vector2.one; collider.size = Vector2.one * 2; } } } Vector2Int firstRoadCoord = directions [0] == Direction.North || directions[0] == Direction.East ? roadStarts [0] : roadEnds [0]; firstRoadCoord -= boundsMin; int lastRoadIndex = roomsNumber - 2; Vector2Int lastRoadCoord = directions [lastRoadIndex] == Direction.North || directions[lastRoadIndex] == Direction.East ? roadEnds [lastRoadIndex] : roadStarts [lastRoadIndex]; lastRoadCoord -= boundsMin; Vector3 playerPosition = new Vector3( firstRoadCoord.x * grid.cellSize.x, firstRoadCoord.y * grid.cellSize.y, 0 ) + cellCenterOffset; Vector3 rescueePosition = new Vector3( lastRoadCoord.x * grid.cellSize.x, lastRoadCoord.y * grid.cellSize.y, 0 ) + cellCenterOffset; Vector3[] enemyPositions = enemyCoordinates.ToArray(); return(new StartCoordinates(playerPosition, rescueePosition, enemyPositions, walkableMap)); }