void GenerateBuildingRooms(ArrayGrid <MapElement> map, Vector2Int buildingPos, Vector2Int roomSize) { Rect building = new Rect(); Rect smaller = new Rect(); buildingPos.x++; buildingPos.y++; building.position = buildingPos; building.size = roomSize; smaller = building; smaller.min = smaller.min + Vector2Int.one; smaller.max = smaller.max - Vector2Int.one; map.FillArea(building, defaultBuildingWallElement); map.FillArea(smaller, defaultRoomElement); AddRecursiveRooms(map, defaultBuildingWallElement, new Vector2Int(innerBuildingRoomSize, innerBuildingRoomSize), smaller); // add a door leading out (improve to lead to nearest road) if (GameRNG.CoinToss()) { if (GameRNG.CoinToss()) { int x = (int)building.min.x + GameRNG.Rand((int)roomSize.x - 2) + 1; int y = (int)building.min.y; map[x, y] = defaultDoorElement; } else { int x = (int)building.min.x + GameRNG.Rand((int)roomSize.x - 2) + 1; int y = (int)building.max.y - 1; map[x, y] = defaultDoorElement; } } else { if (GameRNG.CoinToss()) { int x = (int)building.min.x; int y = (int)building.min.y + GameRNG.Rand((int)roomSize.y - 2) + 1; map[x, y] = defaultDoorElement; } else { int x = (int)building.max.x - 1; int y = (int)building.min.y + GameRNG.Rand((int)roomSize.y - 2) + 1; map[x, y] = defaultDoorElement; } } }
private bool GenerateBuildings(ArrayGrid <MapElement> map) { while (buildingCount < requiredMinNumberOfBuildings) { Vector2Int roomSize = new Vector2Int((int)(buildingSizeLimitX.Max * 2), (int)(buildingSizeLimitY.Max * 2)); int bufferSize = 2; for (;;) { Vector2Int newBuildingAreaSize = new Vector2Int(roomSize.x + bufferSize, roomSize.y + bufferSize); Vector2Int buildingPos = Vector2Int.zero; bool result = map.GetPositionOfRandomAreaOfType(defaultFillElement, newBuildingAreaSize, ref buildingPos); if (result) { //Debug.Log("Creating a building at " + buildingPos + " of size " + roomSize); GenerateBuildingRooms(map, buildingPos, roomSize); //map.FillArea(new Rect(buildingPos, roomSize), defaultDoorElement); //map.FillArea(new Rect(buildingPos, Vector2.one), defaultTreeElement); buildingCount++; if (buildingCount >= requiredMinNumberOfBuildings) { return(true); } } else { if (GameRNG.CoinToss()) { roomSize.x -= 1; } else { roomSize.y -= 1; } //time to start over if (roomSize.x < buildingSizeLimitX.Min || roomSize.y < buildingSizeLimitY.Min) { Debug.Log("starting over because building size is too small and we don't have enough buildings. " + roomSize + " rooms so far: " + buildingCount); buildingCount = 0; generationAttempts++; return(false); } } } } return(false); }
protected bool AddCorridor(ArrayGrid <MapElement> map, ArrayGrid <int> valueMap, KeyValuePair <Vector2Int, Vector2Int> closestCells, bool straightConnections = false) { if (!map.IsValidPosition(closestCells.Key) || !map.IsValidPosition(closestCells.Value)) { return(false); } // we start from both sides Vector2Int p0 = closestCells.Key; Vector2Int p1 = closestCells.Value; Vector2Int dir = new Vector2Int(p0.x > p1.x ? -1 : 1, p0.y > p1.y ? -1 : 1); bool firstHorizontal = GameRNG.CoinToss(); bool secondHorizontal = GameRNG.CoinToss(); for (; ;) { if (!straightConnections) { firstHorizontal = GameRNG.CoinToss(); secondHorizontal = GameRNG.CoinToss(); } // connect rooms dir.x = p0.x > p1.x ? -1 : 1; dir.y = p0.y > p1.y ? -1 : 1; if (p0.x != p1.x && p0.y != p1.y) { if (firstHorizontal) { p0.x += dir.x; } else { p0.y += dir.y; } } if (p0.x != p1.x && p0.y != p1.y) { if (secondHorizontal) { p1.x -= dir.x; } else { p1.y -= dir.y; } } if (valueMap[p0] == valueWall) { valueMap[p0] = valueHall; } if (valueMap[p1] == valueWall) { valueMap[p1] = valueHall; } // connect corridors if on the same level if (p0.x == p1.x) { dir.y = p0.y > p1.y ? -1 : 1; while (p0.y != p1.y) { p0.y += dir.y; if (valueMap[p0] == valueWall) { valueMap[p0] = valueHall; } } if (valueMap[p0] == valueWall) { valueMap[p0] = valueHall; } return(true); } if (p0.y == p1.y) { dir.x = p0.x > p1.x ? -1 : 1; while (p0.x != p1.x) { p0.x += dir.x; if (valueMap[p0] == valueWall) { valueMap[p0] = valueHall; } } if (valueMap[p0] == valueWall) { valueMap[p0] = valueHall; } return(true); } } //return true; }
protected void ConnectClosestRooms(ArrayGrid <MapElement> map, ArrayGrid <int> valueMap, bool withDoors, bool straightConnections = false) { int roomCount = 0; FillDisconnectedRoomsWithDifferentValues(map, valueMap, ref roomCount); List <List <Vector2Int> > rooms = (new List <Vector2Int> [roomCount]).ToList(); for (int i = 0; i < rooms.Count; ++i) { rooms[i] = new List <Vector2Int>(); } IEnumerator <Vector2Int> mapIter = IterateOverMap(map); while (mapIter.MoveNext()) { Vector2Int current = mapIter.Current; if (valueMap[current] != valueWall) { if (valueMap.GetAdjacentElementsOfType(current, false, valueWall).Count > 0) { int roomIndex = valueMap[current] - 1; rooms[roomIndex].Add(current); } } } if (rooms.Count < 2) { return; } // for warshall algorithm // set the connection matrix GameRNG.RandomShuffle(rooms); List <List <bool> > roomConnections = (new List <bool> [rooms.Count]).ToList(); List <List <bool> > transitiveClosure = (new List <bool> [rooms.Count]).ToList();; List <List <int> > distanceMatrix = (new List <int> [rooms.Count]).ToList(); List <List <KeyValuePair <Vector2Int, Vector2Int> > > closestCellsMatrix = (new List <KeyValuePair <Vector2Int, Vector2Int> > [rooms.Count]).ToList(); for (int a = 0; a < rooms.Count; ++a) { roomConnections[a] = (new bool[rooms.Count]).ToList(); transitiveClosure[a] = (new bool[rooms.Count]).ToList(); distanceMatrix[a] = (new int[rooms.Count]).ToList(); closestCellsMatrix[a] = (new KeyValuePair <Vector2Int, Vector2Int> [rooms.Count]).ToList(); for (int b = 0; b < rooms.Count; ++b) { roomConnections[a][b] = false; distanceMatrix[a][b] = int.MaxValue; } } IEnumerator <Vector2Int> roomAreas = IterateOverArea(rooms.Count, rooms.Count); // find the closest cells for each room - Random closest cell while (roomAreas.MoveNext()) { Vector2Int ci = roomAreas.Current; if (ci.x == ci.y) { continue; } KeyValuePair <Vector2Int, Vector2Int> closestCells = new KeyValuePair <Vector2Int, Vector2Int>(); foreach (Vector2Int cellA in rooms[ci.y]) { foreach (Vector2Int cellB in rooms[ci.x]) { int distAB = Mathf.CeilToInt((cellA - cellB).magnitude); if ((distAB < distanceMatrix[ci.y][ci.x]) || (distAB == distanceMatrix[ci.y][ci.x] && GameRNG.CoinToss())) { closestCells = new KeyValuePair <Vector2Int, Vector2Int>(cellA, cellB); distanceMatrix[ci.y][ci.x] = distAB; } } } closestCellsMatrix[ci.y][ci.x] = closestCells; } // Now connect the rooms to the closest ones for (int roomA = 0; roomA < rooms.Count; ++roomA) { int minDist = int.MaxValue; int closestRoom = 0; for (int roomB = 0; roomB < rooms.Count; ++roomB) { if (roomA == roomB) { continue; } int distance = distanceMatrix[roomA][roomB]; if (distance < minDist) { minDist = distance; closestRoom = roomB; } } // connect roomA to closest one KeyValuePair <Vector2Int, Vector2Int> closestCells = closestCellsMatrix[roomA][closestRoom]; if (!roomConnections[roomA][closestRoom] && AddCorridor(map, valueMap, closestCells, straightConnections)) { roomConnections[roomA][closestRoom] = true; roomConnections[closestRoom][roomA] = true; } } // The closest rooms connected. Connect the rest until all areas are connected for (int toConnectA = 0; toConnectA != -1;) { roomAreas = IterateOverArea(rooms.Count, rooms.Count); while (roomAreas.MoveNext()) { Vector2Int ci = roomAreas.Current; transitiveClosure[ci.y][ci.x] = roomConnections[ci.y][ci.x]; } roomAreas = IterateOverArea(rooms.Count, rooms.Count); while (roomAreas.MoveNext()) { Vector2Int ci = roomAreas.Current; if (transitiveClosure[ci.y][ci.x] == true && ci.y != ci.x) { for (int ciZ = 0; ciZ < rooms.Count; ++ciZ) { if (transitiveClosure[ci.x][ciZ] == true) { transitiveClosure[ci.y][ciZ] = true; transitiveClosure[ciZ][ci.y] = true; } } } } toConnectA = -1; roomAreas = IterateOverArea(rooms.Count, rooms.Count); while (roomAreas.MoveNext() && toConnectA == -1) { Vector2Int ci = roomAreas.Current; if (transitiveClosure[ci.y][ci.x] == false && ci.x != ci.y) { toConnectA = ci.y; break; } } if (toConnectA != -1) { int toConnectB = toConnectA; while (toConnectB == toConnectA) { toConnectB = GameRNG.Rand(rooms.Count); } KeyValuePair <Vector2Int, Vector2Int> closestCells = closestCellsMatrix[toConnectA][toConnectB]; AddCorridor(map, valueMap, closestCells, straightConnections); { roomConnections[toConnectA][toConnectB] = true; roomConnections[toConnectB][toConnectA] = true; } } } }
bool GenerateMap(ArrayGrid <MapElement> map, ArrayGrid <int> valueMap) { Vector2Int s0 = Vector2Int.zero; Vector2Int s1 = Vector2Int.zero; Vector2Int p0 = Vector2Int.zero; Vector2Int p1 = Vector2Int.zero; Vector2Int p2 = Vector2Int.zero; Vector2Int diff = Vector2Int.zero; List <Rect> rooms = new List <Rect>(); List <int> roomTypes = new List <int>(); // Place rooms for (int i = 0; i < maxNumberOfRooms; ++i) { // size of room s1 = new Vector2Int(roomSizeLimitX.RandomValuei(), roomSizeLimitY.RandomValuei()); bool result = map.GetPositionOfRandomAreaOfType(defaultWallElement, s1 + roomSizeOffset, ref p0); if (result) { p0 = p0.AddScalar(2); // Connect the room to existing one if (rooms.Count > 0) { int selectedRoom = GameRNG.Rand(rooms.Count); Rect r = rooms[selectedRoom]; // center of this room p1 = p0 + s1.DivideBy(2); // center of second room p2 = Vector2Int.FloorToInt(r.center); // found the way to connect rooms diff = p2 - p1; diff.x = Mathf.Abs(diff.x); diff.y = Mathf.Abs(diff.y); s0 = p1; while (!(diff.x == 0 && diff.y == 0)) { if (GameRNG.Rand(diff.x + diff.y) < diff.x)// move horizontally { diff.x--; s0.x += (s0.x > p2.x ? -1 : 1); } else { diff.y--; s0.y += (s0.y > p2.y ? -1 : 1); } // Check what is on that position if (map[s0] == defaultRoomElement) { break; } else if (map[s0] == defaultCorridorElement) { if (GameRNG.CoinToss()) { break; } } map[s0] = defaultCorridorElement; } } // add to list of rooms Rect roomRect = Rect.MinMaxRect(p0.x, p0.y, p0.x + s1.x, p0.y + s1.y); rooms.Add(roomRect); roomTypes.Add(i); // draw_room int roomType = GameRNG.Rand(4); //select the type of room we should generate if (s1.x == s1.y) { roomType = 3; } if (roomType != 2) { IEnumerator <Vector2Int> areaIter = Mathnv.GetAreaEnumerator(s1); while (areaIter.MoveNext()) { Vector2Int current = areaIter.Current; if (roomType == 0 || roomType == 1)// rectangle room { map[p0 + current] = defaultRoomElement; } else// round room { if (Vector2Int.Distance(s1.DivideBy(2), current) < s1.x / 2) { map[p0 + current] = defaultRoomElement; } } } } else //diamond { IEnumerator <Vector2Int> areaIter = Mathnv.GetAreaEnumerator(s1.DivideBy(2) + Vector2Int.one); while (areaIter.MoveNext()) { Vector2Int current = areaIter.Current; if (current.y >= current.x) { int x0 = p0.x + current.x + s1.DivideBy(2).x; int x1 = p0.x + s1.DivideBy(2).x - current.x; int y0 = p0.y + current.y; int y1 = p0.y + s1.y - current.y; map[x0, y0] = defaultRoomElement; map[x0, y1] = defaultRoomElement; map[x1, y0] = defaultRoomElement; map[x1, y1] = defaultRoomElement; } } } } // end of room addition } return(true); }
void AddRecursiveRooms(ArrayGrid <MapElement> map, MapElement roomElement, Vector2Int minRoomSize, Rect room, bool withDoors = true) { int sizeX = (int)room.size.x; if (sizeX % 2 != 0) { sizeX -= GameRNG.CoinToss() ? 1 : 0; } int sizeY = (int)room.size.y; if (sizeY % 2 != 0) { sizeY -= GameRNG.CoinToss() ? 1 : 0; } bool splitHorizontal = false; if (sizeY * 4 > sizeX) { splitHorizontal = true; } if (sizeX * 4 > sizeY) { splitHorizontal = false; } else { splitHorizontal = GameRNG.CoinToss(); } if (splitHorizontal) { if (sizeY / 2 < minRoomSize.y) { return; } int split = sizeY / 2 + GameRNG.Rand(sizeY / 2 - (int)minRoomSize.y); for (int x = (int)room.min.x; x < (int)room.max.x; ++x) { map[x, (int)room.min.y + split] = roomElement; } if (withDoors) { map[(int)room.min.x + GameRNG.Rand(sizeX - 1) + 1, (int)room.min.y + split] = defaultDoorElement; } Rect newRoom = room; Vector2Int newMax = newRoom.max.ToInt(); newMax.y = room.min.ToInt().y + split; newRoom.max = newMax; AddRecursiveRooms(map, roomElement, minRoomSize, newRoom, withDoors); newRoom = room; Vector2Int newMin = newRoom.min.ToInt(); newMin.y = room.min.ToInt().y + split; newRoom.min = newMin; AddRecursiveRooms(map, roomElement, minRoomSize, newRoom, withDoors); } else { if (sizeX / 2 < minRoomSize.x) { return; } int split = sizeX / 2 + GameRNG.Rand(sizeX / 2 - (int)minRoomSize.x); for (int y = (int)room.min.y; y < (int)room.max.y; ++y) { map[(int)room.min.x + split, y] = roomElement; } if (withDoors) { map[(int)room.min.x + split, (int)room.min.y + GameRNG.Rand(sizeY - 1) + 1] = defaultDoorElement; } Rect newRoom = room; Vector2Int newMax = newRoom.max.ToInt(); newMax.x = room.min.ToInt().x + split; newRoom.max = newMax; AddRecursiveRooms(map, roomElement, minRoomSize, newRoom, withDoors); newRoom = room; Vector2Int newMin = newRoom.min.ToInt(); newMin.x = room.min.ToInt().x + split; newRoom.min = newMin; AddRecursiveRooms(map, roomElement, minRoomSize, newRoom, withDoors); } }