void MakeRoughWall(TileChunkData[,] chunkMap) { int map_size = chunkMap.GetLength(0); for (int y = 0; y < map_size; y++) { for (int x = 0; x < map_size; x++) { TileChunkData tile = chunkMap[y, x]; foreach (var direction in directions) { // 길이다 > 벽을 세우면 안됨 // 길이 아니다. // 그 방향에 타일이 없다. > 벽 있어도 되고 없어도 됨 // 그 방향에 있는 타일이 벽을 세워놨다. > 벽 있어도 되고 없어도 됨 // 그 방향에 있는 타일이 아직 벽을 안세워놨다. > 벽을 세워야 함 // 길이다 > 벽을 세우면 안됨 if ((tile.path_ID & (int)Mathf.Pow(2, directions.IndexOf(direction))) != 0) { continue; } // 길이 아니다. else { Vector2Int v = tile.pos + direction; // 그 방향에 타일이 없다. > 벽 있어도 되고 없어도 됨 if (v.x < 0 || v.x >= map_size || v.y < 0 || v.y >= map_size) { // tile.wall_ID += Random.Range(0, 2) * (int)Mathf.Pow(2, directions.IndexOf(direction)); continue; } TileChunkData next = chunkMap[v.y, v.x]; // 그 방향에 있는 타일이 벽을 세워놨다. > 벽 있어도 되고 없어도 됨 if ((next.wall_ID & (int)Mathf.Pow(2, directions.IndexOf(-direction))) != 0) { // tile.wall_ID += Random.Range(0, 2) * (int)Mathf.Pow(2, directions.IndexOf(direction)); } // 그 방향에 있는 타일이 아직 벽을 안세워놨다. > 벽을 세워야 함 else { tile.wall_ID += (int)Mathf.Pow(2, directions.IndexOf(direction)); } } } } } }
/// <summary> /// 타일 박스를 초기화, /// 순회 과정에서 외곽 막아버림 /// </summary> /// <param name="chunk_map">맵, 청크 2차원 배열</param> void InitChunkMap(TileChunkData[,] chunk_map) { int map_size = chunk_map.GetLength(0); for (int y = 0; y < map_size; y++) { for (int x = 0; x < map_size; x++) { TileChunkData tile = new TileChunkData(x, y); // 타일 위치와 4방위를 검사하여, 맵을 넘어가는 방향을 막아버린다. foreach (var direction in directions) { Vector2Int v = tile.pos + direction; if (!(v.x < 0 || v.x >= map_size || v.y < 0 || v.y >= map_size)) { tile.availableDirections.Add(direction); } } chunk_map[y, x] = tile; } } }
/// <summary> /// 인자로 받은 chunk_map으로 /// </summary> /// <param name="chunk_map"></param> void GenerateMazeData(TileChunkData[,] chunk_map) { int map_size = chunk_map.GetLength(0); Vector2Int nextPos; TileChunkData currentTile = chunk_map[map_size - 1, 0]; // 입구와 연결되도록 길을 뚫어준다. currentTile.path_ID += 4; Stack <TileChunkData> visited_Tiles = new Stack <TileChunkData>(); int visited_count = 1; int all_tile_count = map_size * map_size; // 미로 생성 알고리즘. 완료될때까지 반복 while (true) { currentTile.visited = true; for (int i = currentTile.availableDirections.Count - 1; i >= 0; i--) { var pos = currentTile.pos + currentTile.availableDirections[i]; if (chunk_map[pos.y, pos.x].visited) { currentTile.availableDirections.RemoveAt(i); } } //더 이상 갈 방향이 없을 때 if (currentTile.availableDirections.Count == 0) { //모든 타일이 할당 되었을 때 if (visited_count == all_tile_count) { break; // end } //이전 타일로 돌아감 else { currentTile = visited_Tiles.Pop(); } } //갈 수 있는 방향 중 하나를 선택. else { //방향 선택하고 현재 타일에서 해당 방향 제거 Vector2Int direction = currentTile.availableDirections[Random.Range(0, currentTile.availableDirections.Count)]; nextPos = currentTile.pos + direction; currentTile.availableDirections.Remove(direction); currentTile.path_ID += (int)Mathf.Pow(2, directions.IndexOf(direction)); //stack에 push 후 타일 이동. visited_Tiles.Push(currentTile); visited_count++; //이전에 있던 타일로 가는 길 제거 currentTile = chunk_map[nextPos.y, nextPos.x]; currentTile.path_ID += (int)Mathf.Pow(2, directions.IndexOf(-direction)); } } }
/// <summary> /// 청크 맵을 생성한다. /// </summary> /// <param name="map_size"></param> /// <returns></returns> public FieldData GenerateChunkMap(int map_size = 3, bool isAttacked = false) { TileChunkData[,] chunkMap = new TileChunkData[map_size, map_size]; //청크 맵 초기화 InitChunkMap(chunkMap); //미로 데이터 생성 GenerateMazeData(chunkMap); //생성된 미로 데이터를 기반으로 벽의 다양성을 부여. MakeRoughWall(chunkMap); //생성된 미로 데이터를 기반으로 필드 채우기 for (int i = 0; i < map_size; i++) { for (int j = 0; j < map_size; j++) { TileChunkData tile = chunkMap[i, j]; /// 여기서 파일 불러와서 아이디 적용. tile.SetFieldData(MakeWallChunk(15 - tile.wall_ID)); } } FieldData wallData = MergeChunkDatasToFieldData(chunkMap); int fieldSize = map_size * 4; FieldData fieldData = new FieldData(fieldSize, fieldSize); StringBuilder sb = new StringBuilder(); Vector2Int pos = new Vector2Int(Random.Range(0, 100), Random.Range(0, 100)); float scale = Random.Range(0.1f, 0.3f); float intensity = GetPerlinNoiseIntensity(fieldSize, pos, scale, 20); List <string> tiles = new List <string>() { "TN", "HL", "PW", "WT" }; string tile1 = tiles[Random.Range(0, tiles.Count)]; tiles.Remove(tile1); string tile2 = tiles[Random.Range(0, tiles.Count)]; // 가시 타일 생성 for (int y = 0; y < fieldSize; y++) { for (int x = 0; x < fieldSize; x++) { if ((Mathf.PerlinNoise((x + pos.x) * scale, (y + pos.y) * scale)) < intensity) { sb.Append(tile1); } else { sb.Append("FL"); } } } fieldData.fieldStrData = sb.ToString(); char[] temp = wallData.fieldStrData.ToCharArray(); for (int i = 0; i < fieldSize * fieldSize; i++) { // char wc1 = wallData.fieldStrData[i*2]; // char wc2 = wallData.fieldStrData[i*2+1]; char fc1 = fieldData.fieldStrData[i * 2]; char fc2 = fieldData.fieldStrData[i * 2 + 1]; if ($"{fc1}{fc2}" != "FL") { temp[i * 2] = fc1; temp[i * 2 + 1] = fc2; } } #region 워 타일 생성 sb = new StringBuilder(); pos = new Vector2Int(Random.Range(0, 100), Random.Range(0, 100)); scale = Random.Range(0.1f, 0.3f); intensity = GetPerlinNoiseIntensity(fieldSize, pos, scale, 20); for (int y = 0; y < fieldSize; y++) { for (int x = 0; x < fieldSize; x++) { if ((Mathf.PerlinNoise((x + pos.x) * scale, (y + pos.y) * scale)) < intensity) { sb.Append(tile2); } else { sb.Append("FL"); } } } fieldData.fieldStrData = sb.ToString(); for (int i = 0; i < fieldSize * fieldSize; i++) { // char wc1 = wallData.fieldStrData[i*2]; // char wc2 = wallData.fieldStrData[i*2+1]; char fc1 = fieldData.fieldStrData[i * 2]; char fc2 = fieldData.fieldStrData[i * 2 + 1]; if ($"{fc1}{fc2}" != "FL") { temp[i * 2] = fc1; temp[i * 2 + 1] = fc2; } } #endregion #region Void 타일 생성 sb = new StringBuilder(); pos = new Vector2Int(Random.Range(0, 100), Random.Range(0, 100)); scale = Random.Range(0.1f, 0.3f); intensity = GetPerlinNoiseIntensity(fieldSize, pos, scale, 20); for (int y = 0; y < fieldSize; y++) { for (int x = 0; x < fieldSize; x++) { if ((Mathf.PerlinNoise((x + pos.x) * scale, (y + pos.y) * scale)) < intensity) { sb.Append("HO"); } else { sb.Append("FL"); } } } fieldData.fieldStrData = sb.ToString(); for (int i = 0; i < fieldSize * fieldSize; i++) { char wc1 = wallData.fieldStrData[i * 2]; char wc2 = wallData.fieldStrData[i * 2 + 1]; char fc1 = fieldData.fieldStrData[i * 2]; char fc2 = fieldData.fieldStrData[i * 2 + 1]; if ($"{wc1}{wc2}" == "WL" && $"{fc1}{fc2}" == "HO") { temp[i * 2] = fc1; temp[i * 2 + 1] = fc2; } } #endregion // 기습당하면 랜덤 위치에서 시작 if (isAttacked) { List <Vector2Int> randomPositions = new List <Vector2Int>(); while (randomPositions.Count < 4) { Vector2Int tempPosition = new Vector2Int(Random.Range(0, fieldSize), Random.Range(0, fieldSize)); if (!randomPositions.Contains(tempPosition)) { randomPositions.Add(tempPosition); temp[(tempPosition.x + tempPosition.y * fieldSize) * 2 + 0] = 'S'; temp[(tempPosition.x + tempPosition.y * fieldSize) * 2 + 1] = 'T'; } } } // 기습 안당하면 왼쪽 아래에서 시작 else { // 계단생성. temp[fieldSize * (fieldSize - 2) * 2 + 0] = 'S'; temp[fieldSize * (fieldSize - 2) * 2 + 1] = 'T'; temp[fieldSize * (fieldSize - 2) * 2 + 2] = 'S'; temp[fieldSize * (fieldSize - 2) * 2 + 3] = 'T'; temp[fieldSize * (fieldSize - 1) * 2 + 0] = 'S'; temp[fieldSize * (fieldSize - 1) * 2 + 1] = 'T'; temp[fieldSize * (fieldSize - 1) * 2 + 2] = 'S'; temp[fieldSize * (fieldSize - 1) * 2 + 3] = 'T'; } wallData.fieldStrData = new string(temp); return(wallData); }