マップ自動生成時に発生する孤立部屋について

C#

1using UnityEngine;2using System.Collections;3using System.Collections.Generic;4 5public class MapGenerator 6{7 8 private const int MINIMUM_RANGE_WIDTH = 15;9 10 private int mapSizeX;11 private int mapSizeY;12 private int maxRoom;13 14 private List<Range> roomList = new List<Range>();15 private List<Range> rangeList = new List<Range>();16 private List<Range> passList = new List<Range>();17 private List<Range> roomPassList = new List<Range>();18 19 private bool isGenerated = false;20 21 public int[,] GenerateMap(int mapSizeX, int mapSizeY, int maxRoom)22 {23 this.mapSizeX = mapSizeX;24 this.mapSizeY = mapSizeY;25 26 int[,] map = new int[mapSizeX, mapSizeY];27 28 CreateRange(maxRoom);29 CreateRoom();30 31 // ここまでの結果を一度配列に反映する32 foreach (Range pass in passList)33 {34 for (int x = pass.Start.X; x <= pass.End.X; x++)35 {36 for (int y = pass.Start.Y; y <= pass.End.Y; y++)37 {38 map[x, y] = 1;39 }40 }41 }42 foreach (Range roomPass in roomPassList)43 {44 for (int x = roomPass.Start.X; x <= roomPass.End.X; x++)45 {46 for (int y = roomPass.Start.Y; y <= roomPass.End.Y; y++)47 {48 map[x, y] = 1;49 }50 }51 }52 foreach (Range room in roomList)53 {54 for (int x = room.Start.X; x <= room.End.X; x++)55 {56 for (int y = room.Start.Y; y <= room.End.Y; y++)57 {58 map[x, y] = 1;59 }60 }61 }62 63 TrimPassList(ref map);64 65 return map;66 }67 68 public void CreateRange(int maxRoom)69 {70 // 区画のリストの初期値としてマップ全体を入れる71 rangeList.Add(new Range(0, 0, mapSizeX - 1, mapSizeY - 1));72 73 bool isDevided;74 do75 {76 // 縦 → 横 の順番で部屋を区切っていく。一つも区切らなかったら終了77 isDevided = DevideRange(false);78 isDevided = DevideRange(true) || isDevided;79 80 // もしくは最大区画数を超えたら終了81 if (rangeList.Count >= maxRoom)82 {83 break;84 }85 } while (isDevided);86 87 }88 89 public bool DevideRange(bool isVertical)90 {91 bool isDevided = false;92 93 // 区画ごとに切るかどうか判定する94 List<Range> newRangeList = new List<Range>();95 foreach (Range range in rangeList)96 {97 // これ以上分割できない場合はスキップ98 if (isVertical && range.GetWidthY() < MINIMUM_RANGE_WIDTH * 2 + 1)99 {100 continue;101 }102 else if (!isVertical && range.GetWidthX() < MINIMUM_RANGE_WIDTH * 2 + 1)103 {104 continue;105 }106 107 System.Threading.Thread.Sleep(1);108 109 // 40%の確率で分割しない110 // ただし、区画の数が1つの時は必ず分割する111 if (rangeList.Count > 1 && RogueUtils.RandomJadge(0.4f))112 {113 continue;114 }115 116 // 長さから最少の区画サイズ2つ分を引き、残りからランダムで分割位置を決める117 int length = isVertical ? range.GetWidthY() : range.GetWidthX();118 int margin = length - MINIMUM_RANGE_WIDTH * 2;119 int baseIndex = isVertical ? range.Start.Y : range.Start.X;120 int devideIndex = baseIndex + MINIMUM_RANGE_WIDTH + RogueUtils.GetRandomInt(1, margin) - 1;121 122 // 分割された区画の大きさを変更し、新しい区画を追加リストに追加する123 // 同時に、分割した境界を通路として保存しておく124 Range newRange = new Range();125 if (isVertical)126 {127 passList.Add(new Range(range.Start.X, devideIndex, range.End.X, devideIndex));128 newRange = new Range(range.Start.X, devideIndex + 1, range.End.X, range.End.Y);129 range.End.Y = devideIndex - 1;130 }131 else132 {133 passList.Add(new Range(devideIndex, range.Start.Y, devideIndex, range.End.Y));134 newRange = new Range(devideIndex + 1, range.Start.Y, range.End.X, range.End.Y);135 range.End.X = devideIndex - 1;136 }137 138 // 追加リストに新しい区画を退避する。139 newRangeList.Add(newRange);140 141 isDevided = true;142 }143 144 // 追加リストに退避しておいた新しい区画を追加する。145 rangeList.AddRange(newRangeList);146 147 return isDevided;148 }149 150 private void CreateRoom()151 {152 // 部屋のない区画が偏らないようにリストをシャッフルする153 rangeList.Sort((a, b) => RogueUtils.GetRandomInt(0, 1) - 1);154 155 // 1区画あたり1部屋を作っていく。作らない区画もあり。156 foreach (Range range in rangeList)157 {158 System.Threading.Thread.Sleep(1);159 // 30%の確率で部屋を作らない160 // ただし、最大部屋数の半分に満たない場合は作る161 if (roomList.Count > maxRoom / 2 && RogueUtils.RandomJadge(0.3f))162 {163 continue;164 }165 166 // 猶予を計算167 int marginX = range.GetWidthX() - MINIMUM_RANGE_WIDTH + 1;168 int marginY = range.GetWidthY() - MINIMUM_RANGE_WIDTH + 1;169 170 // 開始位置を決定171 int randomX = RogueUtils.GetRandomInt(1, marginX);172 int randomY = RogueUtils.GetRandomInt(1, marginY);173 174 // 座標を算出175 int startX = range.Start.X + randomX;176 int endX = range.End.X - RogueUtils.GetRandomInt(0, (marginX - randomX)) - 1;177 int startY = range.Start.Y + randomY;178 int endY = range.End.Y - RogueUtils.GetRandomInt(0, (marginY - randomY)) - 1;179 180 // 部屋リストへ追加181 Range room = new Range(startX, startY, endX, endY);182 roomList.Add(room);183 184 // 通路を作る185 CreatePass(range, room);186 }187 }188 189 private void CreatePass(Range range, Range room)190 {191 List<int> directionList = new List<int>();192 if (range.Start.X != 0)193 {194 // Xマイナス方向195 directionList.Add(0);196 }197 if (range.End.X != mapSizeX - 1)198 {199 // Xプラス方向200 directionList.Add(1);201 }202 if (range.Start.Y != 0)203 {204 // Yマイナス方向205 directionList.Add(2);206 }207 if (range.End.Y != mapSizeY - 1)208 {209 // Yプラス方向210 directionList.Add(3);211 }212 213 // 通路の有無が偏らないよう、リストをシャッフルする214 directionList.Sort((a, b) => RogueUtils.GetRandomInt(0, 1) - 1);215 216 bool isFirst = true;217 foreach (int direction in directionList)218 {219 System.Threading.Thread.Sleep(1);220 // 80%の確率で通路を作らない221 // ただし、まだ通路がない場合は必ず作る222 if (!isFirst && RogueUtils.RandomJadge(0.8f))223 {224 continue;225 }226 else227 {228 isFirst = false;229 }230 231 // 向きの判定232 int random;233 switch (direction)234 {235 case 0: // Xマイナス方向236 random = room.Start.Y + RogueUtils.GetRandomInt(1, room.GetWidthY()) - 1;237 roomPassList.Add(new Range(range.Start.X, random, room.Start.X - 1, random));238 break;239 240 case 1: // Xプラス方向241 random = room.Start.Y + RogueUtils.GetRandomInt(1, room.GetWidthY()) - 1;242 roomPassList.Add(new Range(room.End.X + 1, random, range.End.X, random));243 break;244 245 case 2: // Yマイナス方向246 random = room.Start.X + RogueUtils.GetRandomInt(1, room.GetWidthX()) - 1;247 roomPassList.Add(new Range(random, range.Start.Y, random, room.Start.Y - 1));248 break;249 250 case 3: // Yプラス方向251 random = room.Start.X + RogueUtils.GetRandomInt(1, room.GetWidthX()) - 1;252 roomPassList.Add(new Range(random, room.End.Y + 1, random, range.End.Y));253 break;254 }255 }256 257 }258 259 private void TrimPassList(ref int[,] map)260 {261 // どの部屋通路からも接続されなかった通路を削除する262 for (int i = passList.Count - 1; i >= 0; i--)263 {264 Range pass = passList[i];265 266 bool isVertical = pass.GetWidthY() > 1;267 268 // 通路が部屋通路から接続されているかチェック269 bool isTrimTarget = true;270 if (isVertical)271 {272 int x = pass.Start.X;273 for (int y = pass.Start.Y; y <= pass.End.Y; y++)274 {275 if (map[x - 1, y] == 1 || map[x + 1, y] == 1)276 {277 isTrimTarget = false;278 break;279 }280 }281 }282 else283 {284 int y = pass.Start.Y;285 for (int x = pass.Start.X; x <= pass.End.X; x++)286 {287 if (map[x, y - 1] == 1 || map[x, y + 1] == 1)288 {289 isTrimTarget = false;290 break;291 }292 }293 }294 295 // 削除対象となった通路を削除する296 if (isTrimTarget)297 {298 passList.Remove(pass);299 300 // マップ配列からも削除301 if (isVertical)302 {303 int x = pass.Start.X;304 for (int y = pass.Start.Y; y <= pass.End.Y; y++)305 {306 map[x, y] = 0;307 }308 }309 else310 {311 int y = pass.Start.Y;312 for (int x = pass.Start.X; x <= pass.End.X; x++)313 {314 map[x, y] = 0;315 }316 }317 }318 }319 320 // 外周に接している通路を別の通路との接続点まで削除する321 // 上下基準322 for (int x = 0; x < mapSizeX - 1; x++)323 {324 if (map[x, 0] == 1)325 {326 for (int y = 0; y < mapSizeY; y++)327 {328 if (map[x - 1, y] == 1 || map[x + 1, y] == 1)329 {330 break;331 }332 map[x, y] = 0;333 }334 }335 if (map[x, mapSizeY - 1] == 1)336 {337 for (int y = mapSizeY - 1; y >= 0; y--)338 {339 if (map[x - 1, y] == 1 || map[x + 1, y] == 1)340 {341 break;342 }343 map[x, y] = 0;344 }345 }346 }347 // 左右基準348 for (int y = 0; y < mapSizeY - 1; y++)349 {350 if (map[0, y] == 1)351 {352 for (int x = 0; x < mapSizeY; x++)353 {354 if (map[x, y - 1] == 1 || map[x, y + 1] == 1)355 {356 break;357 }358 map[x, y] = 0;359 }360 }361 if (map[mapSizeX - 1, y] == 1)362 {363 for (int x = mapSizeX - 1; x >= 0; x--)364 {365 if (map[x, y - 1] == 1 || map[x, y + 1] == 1)366 {367 break;368 }369 map[x, y] = 0;370 }371 }372 }373 }374 375}

コメントを投稿

0 コメント