발전하는 춘배

[원고, C++/OOP] 로그라이크 게임 만들어보기 6 - 랜덤 맵 생성하기 : BSP 알고리즘, 플레이어 스폰, 몬스터 스폰 본문

.원고

[원고, C++/OOP] 로그라이크 게임 만들어보기 6 - 랜덤 맵 생성하기 : BSP 알고리즘, 플레이어 스폰, 몬스터 스폰

춘배0 2026. 2. 14. 17:54

이제는 로그라이크 느낌이 나도록 제대로 된 맵을 만들어보려고 한다.

맵을 만드는 데에는 여러 방법이 있겠지만 잘 알려진 알고리즘인 BSP를 이용하여 만들어보고자 한다.

BSP 알고리즘: https://nowitzki.tistory.com/9

구현: https://nowitzki.tistory.com/10

이 두 글을 잘 참고했다.

 

1. BSP 알고리즘

BSP는 Binary Space Partitioning의 약자이다.

즉, 전체 공간을 두 부분으로 재귀적으로 나누는 알고리즘이다.

  1. 공간을 두 부분으로 나누며 트리 형태로 저장한다. 시작할 때의 전체 공간은 0번 노드이며, 그를 분할해 나온 두 공간은 0번 노드의 자식인 1, 2번 노드가 된다.
  2. n번 재귀적으로 반복하여 트리를 만든다.
  3. 리프 공간들에 대해 각 공간을 초과하지 않는 방을 만든다.
  4. 트리의 리프 노드부터 올라가면서 두 형제 노드의 방을 서로 잇는 길(통로)을 만들어준다.

이런 느낌 (그림판으로 그림)

 

2. 구현

일단 흐름을 대충 생각해본다. BSP는 맵 생성을 위한 알고리즘일뿐임을 고려한다. 즉, 나중에 내 기분에 따라 맵 생성 알고리즘을 바꿀 수 있는데 그 때 바꿀 코드를 최소화해야한다.

이를 위해 Game에서 World를 만들고, World에서는 오직 bsp.generateMap()을 호출하여 map을 받을 것이다.

Map newMap = bsp.generateMap() 이런느낌

만약 알고리즘을 바꾼다면 뭐 myRandom.generateMap() 이런느낌으로만 바꿔주면됨.

이런 걸 위한 상속용 상위 클래스인 MapGenerator 클래스를 만들 수도 있긴 하겠는데 아직 그단계는 아니니(귀찮으니) 패스.

그래서 퍼블릭 멤버는 얘네뿐이다.

class BSP {
    public:
        BSP(int width, int height, int maxDepth, double minRatio, double maxRatio);
        void generateMap();
    private:
    	...
}

 

내가 하려는 건 알고리즘이 아니므로 내부 구현은 당당히 복붙하려 했는데 내 게임 구조에 맞게 뭔가 수정하려니 이것도 일이다. 쉽진않았다.

 

일단 Map 객체를 하나 만들어야 하는데 예전에 이건 World만 할 수 있도록 해놨었는데 이젠 World도 BSP를 통해서 만들 거기 때문에 friend 지정을 수정해준다.

class Map {
  friend class BSP;
  ...
}

 

World::popluateMap() 수정

void World::populateMap(const std::string &id, int w, int h) {
  BSP bsp(w, h, 3, 0.3, 0.7);
  maps[id] = std::unique_ptr<Map>(bsp.generateMap());
  currentMapId = id;
  currentMap = maps[id].get();

  spawnPlayer();
  spawnMonsters();
}

BSP 알고리즘은 위에 참고했던 포스트 쓴 분이 구현해 놓은 거에서 아주 조금만 수정해서 바로 사용할 수 있게 함

배열을 숫자로 채우는 부분 -> Map 객체를 Tile로 채우기로 변경

 

테스트:

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# @ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # $ . . . . . . . . . . . . . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # $ . . . . . . . . . . . . . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # . . . . . . . . . . . . . . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # . . . . . . . . . . . . . . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # . . . . . . . . . . . . . . . . . . . . . . . . . . # # # # # # # # # # # # # # # # # # # # # 
# # # . . . . . . . . . . . . . . . . . . . # # # # # # . # # # # # # # # # # # # # # # # # # # # # 
# # # . . . . . . . . . . . . . . . . . . . # # # # # # . # # # # # # # # # # # # # # # # # # # # # 
# # # . . . . . . . . . . . . . . . . . . . # # # # # # . # # # # # # # # # # # # # # # # # # # # # 
# # # . . . . . . . . . . . . . . . . . . . # # # # # # . # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # # # # # # # # # # # # # # # . # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # # # # # # # # # # # # # # # . # # # # # . . . . . . . . . # # # # # # # 
# # # # # # # # # # # # . . . . . . . . . # # # # # # # . # # # # # . . . . . . . . . # # # # # # # 
# # # # # # # # # # # # . # # # # # # # . # # # # # # # . # # # # # . . . . . . . . . # # # # # # # 
# # # # # # # # # # # # . # # # # # # # . # # # # # # # . . . . . . . . . . . . . . . # # # # # # # 
# # # # # # # # # # # # . # # # # # # # . # # # # # # # # # # # # # . . . . . . . . . # # # # # # # 
# # # # # # # # # # # # . # # # # # # # . # # # # # # # # # # # # # . . . . . . . . . # # # # # # # 
# # # # # # # # # # # # . # # # # # # # . # # # # # # # # # # # # # # # # # . # # # # # # # # # # # 
# # # # # # # # # # # # . # # # # # # # . # # # # # # # # # # # . . . . . . . # # # # # # # # # # # 
# # # # # # # # # # # # . # # # # # # # . # # # # # # # # # # # . # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # # # # . # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # # # # . # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # . . . . . . . # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # . . . . . . . # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # . . . . . . . # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # . . . . . . . # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # . . . . . . . # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # . . . . . . . # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # . . . . . . . # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # . . . . . . . # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # . . . . . . . # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # . . . . . . . # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # . . . . . . . # # # # # # # # # # # # # # 
# # # # # # # # # # # # . # . . . . . . . . . . . . . # # # # # # # # # # # # # # # # # # # # # # # 
# # # . . . . . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # . # # # # # # # # # . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# . . . . # # # # # # # # . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# . . . . # # # # # # # # . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# . . . . . . . . . . . . . . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# . . . . # # # # # . # # . . . . . . . . . . . . . . . . . . . # # # # # # # . . . . . . # # # # # 
# . . . . # # # # # . . . . . . . . . . . . . . . . . . . . . . # # # # # # # . . . . . . # # # # # 
# # # # # # # # # # # # # . . . . . . . . . . . . . . . . . . . # # # # # # # . . . . . . # # # # # 
# # # # # # # # # # # # # . . . . . . . . . . . . . . . . . . . # # # # # # # . . . . . . # # # # # 
# # # # # # # # # # # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # # # 
# # # # # # # # # # # # # . . . . . . # . . . . . . . . . . . . # # # # # # # . . . . . . # # # # # 
# # # # # # # # # # # # # # # # # # # # . . . . . . . . . . . . # # # # # # # . . . . . . # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

잘 만들어진다

이제 저 이상한 위치에 고정으로 꽃혀있는 플레이어랑 몬스터들을 어떻게 해결해보자.

3. 플레이어 스폰

일단 플레이어 스폰 방식을 대충 구상해보면 이렇다.

BSP의 맵 생성 과정 말단에서 맵의 여러 방 중 어떤 방(지금은 귀찮으니 첫 번째 방)의 중심에 PLAYERSPAWN 타일을 둔다.

WORLD의 popluateMap()에서, 맵에 존재하는 PlayerSpawn 타일 위치를 찾고, 플레이어 객체의 좌표를 그 타일의 좌표로 설정한다. 근데 매번 n*m 맵의 모든 타일을 검사하는 건 비효율적이므로 map 객체의 속성으로 playerspawn (x, y)를 저장해서 바로 찾을 수 있도록 한다.

void World::populateMap(const std::string &id, int w, int h) {
  BSP bsp(w, h, 3, 0.3, 0.7);
  maps[id] = std::unique_ptr<Map>(bsp.generateMap());
  currentMapId = id;
  currentMap = maps[id].get();

  spawnPlayerOnCurrentMap();
  spawnMonsters();
}
Map *BSP::generateMap() {
  srand(time(NULL)); // 난수 생성
  Map *map = new Map(width, height);
  map->fill(TileDatabase::findTileByTileType(TileType::WALL));

  Node *root = new Node(0, 0, width, height, 0); // 트리의 root 노드 생성
  Divide(root, 0);
  GenerateRoom(root, 0);
  GeneratePath(root, 0, map);

  // 공간 정보로 길을 그려주고, 방을 그려주어 방을 통과하는 길이 보이지 않도록
  // 함
  PrintRoom(root, 0, map);

  makeBorderWalls(map);

  // 플레이어 스폰 위치 설정
  makePlayerSpawn(root, map);

  return map;
}
void BSP::makePlayerSpawn(Node *root, Map *map) {
  Node *firstRoom = findFirstRoom(root, 0);
  if (firstRoom != nullptr) {
    int playerX = firstRoom->room.x + firstRoom->room.width / 2;
    int playerY = firstRoom->room.y + firstRoom->room.height / 2;
    map->setTile(playerX, playerY,
                 TileDatabase::findTileByTileType(TileType::PLAYERSPAWN));
    map->setPlayerSpawn(playerX, playerY);
  }
}

 

이거 하면서 느낀건데 TileDB를 만들어 놓으니 Tile 추가 확장성이 확실히 좋다는 걸 느꼈다. json에 타일 데이터 추가해주고, Tiletype enum class 추가해주고, json 파싱을 위한 아래 함수에 한줄만 추가해주면 된다.

TileType TileDatabase::tileTypeFromString(const std::string& s) {
    if (s == "Empty") return TileType::EMPTY;
    if (s == "Floor") return TileType::FLOOR;
    if (s == "Wall")  return TileType::WALL;
    if (s == "Playerspawn") return TileType::PLAYERSPAWN;
    if (s == "Monsterspawn") return TileType::MONSTERSPAWN;
    throw std::runtime_error("Unknown TileType: " + s);
}

 

테스트:

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # $ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # $ # # # # # # # # # # # # # # # . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # . . . . . . . . . # # # # # # # . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # . . . . . . . . . # # # # # # # . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # . . . . . . . . . # # # # # # # . . . # # # # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # . . . . . . . . . # # # # # # # . . . # # # # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # . . . . . . . . . # . . . . . . . . . # # # # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # . . . . @ . . . . . . # # # # # . . . # # # # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # . . . . . . . . . # # # # # # # . . . # # # # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # . . . . . . . . . # # # # # # # . . . # # # # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # . . . . . . . . . # # # # # # # . . . # # # # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # . . . . . . . . . # # # # # # # . . . # # . . . . . . . . . . . . . . . . . # # # # # # # # # 
# # # . . . . . . . . . # # # # # # # # . # # # . # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # . # # # . # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # . # # # . # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # . # # # . # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # . # # # . # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # # # # # # # # # # # # # # # # . . . # # # . # # # # # # # # . . . . . . . . # # # # # # # # # 
# # # # # # # # # # # # # # # # # # . # # # # # . # # # # # # # # # # # # . . . . # # # # # # # # # 
# # # # # # # # # # # # # # # # # # . # # # # # . # # # # # # # # # # # # . # # . # # # # # # # # # 
# # # # # # # # # # # # # # # # # # . # # # # # . # # # # # # # # # # # # . # # . # # # # # # # # # 
# # # # # # # # # # # # # # # # # # . # # # # # . # # # # # # # # # # # # . # # . # # # # # # # # # 
# # # # # # # # # # # # . . . . . . . . . . . . . # # # # # # # # # # # # . # # . # # # # # # # # # 
# # # # # # # # # # # # . . . . . . . . . . . . . # # # # # # # # # # # # . # # . # # # # # # # # # 
# # # # # # # # # # # # . . . . . . . . . . . . . # # # # # # # . . . . . . . . . . . . . . . . # # 
# # # # # # # # # # # # . . . . . . . . . . . . # # # # # # # # . . . . . . . . . . . . . . . . # # 
# # # # # # # # # # # # . . . . . . . . . . . . # # # # # # # # . . . . . . . . . . . . . . . . # # 
# # # # # # # # # # # # # # # # # # . # # # # # # # # # # # # # . . . . . . . . . . . . . . . . # # 
# # # # # # # # # # # # # # # # # # . # # # # # # # # # # # # # . . . . . . . . . . . . . . . . # # 
# # # # # # # # # # # # # # # # # # . # # # # # # # # # # # # # . . . . . . . . . . . . . . . . # # 
# # # # # # # # # # # # # # # # # # . # # # # # # # # # # # # # # # # # # . # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # . # # # # # # # . . . . . . . . . . . . # # # # # # # # # # # # 
# # # # # # # # # # # # # # # . . . . # # # # # # # . # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # . # # # # # # # # # # . # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # . # # # # # # # # # # . # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # . . . . . . . . . . # # # # # # . # # # # # # # # # # # # # . . . . . . # # # # 
# # # # # # # # # # . . . . . . . . . . # # # # # # . # # # # # # # # # # # # # . . . . . . # # # # 
# # # # # # # # # # . . . . . . . . . . # # # # # . . . # # # # # # # # # # # # . . . . . . # # # # 
# # # # # # # # # # . . . . . . . . . . # # # # # . . . # # # # # . . . . . . . . . . . . . # # # # 
# # # # # # # # # # . . . . . . . . . . # # # # # . . . . . . . . . # # # # # # . . . . . . # # # # 
# # # # # # # # # # . . . . . . . . . . # # # # # . . . # # # # # # # # # # # # . . . . . . # # # # 
# # # # # # # # # # . . . . . . . . . . # # # # # . . . # # # # # # # # # # # # . . . . . . # # # # 
# # # # # # # # # # . . . . . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

새로 맵 만들 때마다 첫번째 방의 중앙에 플레이어가 위치한다.

 

4. 몬스터 스폰

몬스터 스폰은 이렇게 해보려 한다.

2, 4, 6, 8 ... 번째 방마다 몬스터를 1~3마리 스폰한다. 스폰 위치는 방 안에서 완전히 랜덤으로 한다. 더 복잡하게 할 이유를 딱히 못 느꼈다. 방식은 플레이어 스폰과 같다. 랜덤한 위치의 타일을 MonsterSpawn 타일로 바꾸고, 맵이 이 MonsterSpawn의 좌표들을 벡터로 저장하여 접근을 빠르게 할 수 있도록 한다.

void BSP::makeMonsterSpawns(Node *node, int depth, Map *map) {
  if (depth == maxDepth - 1) {
    int startX = node->rightNode->room.x;
    int startY = node->rightNode->room.y;
    int endX = startX + node->rightNode->room.width - 1;
    int endY = startY + node->rightNode->room.height - 1;
    std::vector<Point> points = getRandomPoints(startX, startY, endX, endY);
    for (auto &p : points) {
      map->setTile(p.x, p.y,
                   TileDatabase::findTileByTileType(TileType::MONSTERSPAWN));
      map->addMonsterSpawn(p.x, p.y);
    }

    return;
  } else {
    makeMonsterSpawns(node->leftNode, depth + 1, map);
    makeMonsterSpawns(node->rightNode, depth + 1, map);
  }
}
void World::spawnMonsters() {
  auto monsterSpawns = currentMap->getMonsterSpawns();
  for (auto &spawn : monsterSpawns) {
    entities[currentMapId].push_back(
        std::make_unique<Entity>(spawn.first, spawn.second, 'M', 15, 10));
  }
}

 

테스트:

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # . . . . . . . . . . # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # . . . . . . . . . . # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # . . . . . . . . . . # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # . . . . . . . . . M # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # . . . . . . . . . . # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # . . . . . . . . . . # # 
# # # # # # # . . . . . . . . . . . . . . . . . . # # # # # # # # # # # # # . . . . . . . . . . # # 
# # # # # # # . . . . . . . . . . . . . . . . . . # # # # # # # # # # . . . . . . . . . . . . . # # 
# # # # # # # . . . . . . . . . . . . . . . . . . # # # # # # # # # # . # # . . . . . . . . . . # # 
# # # # # # # . . . . . . . . . . . . . . . . . . # # # # # . . . . . . # # . . M . . . . . . . # # 
# # # # # # # . . . . . . . . . @ . . . . . . . . . . . . # . . . . . . # # . . . . . . . . . . # # 
# # # # # # # . . . . . . . . . . . . . . . . . . # # # . # . . . . . . # # . . . . . . . . . . # # 
# # # # # # # . . . . . . . . . . . . . . . . . . # # # . # . . . . . . # # . . . . . . . . . . # # 
# # # # # # # . . . . . . . . . . . . . . . . . . # # # . # . . . . . # # # . . . . . . . . . . # # 
# # # # # # # # # # # # # # # # . # # # # # # # # # # # . # . . . . . # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # . # # # # # # # # # # # . # . . . . . # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # . # # # # # # # # # # # . # # # . # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # . # # # # # # # # # # # . # # # . # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # . . . # # # # # # # # # # # . # # # . . . . . # # # # # # # # # # # # # 
# # # # # # # # # # # # # # . # . # # # # # # # # # # # . # # # # # # # . # # # # # # # # # # # # # 
# # # # # # # # # # # # # # . # . # # # # # # # # # # # . # # # # # # # . # # # # # # # # # # # # # 
# # # # # # # # # # # # # # . # . # # # # # # # # # # # . # # # # # # # . # # # # # # # # # # # # # 
# # # # # # # # # # # # # # . # . # # # # # # # # # # # . # # # . . . . . . . . # # # # # # # # # # 
# # # # # # # # # # . . . . . . . . . # # # # # # # # # . # # # . . . . . . . . # # # # # # # # # # 
# # # # # # # # # # . . . . . . . . . # # # # # # # # # . . . . . . . . . . . . # # # # # # # # # # 
# # # # # # # # # # . . . . . . . . . # # # # # # # # # # # # # . . . . . . . . # # # # # # # # # # 
# # # # # # # # # # . . . . . . . . . # # # # # # # # # # # # # . . . . . . . . # # # # # # # # # # 
# # # # # # # # # # M . . . . . . . . # # # # # # # # # # # # # # # # # . . . . . # # # # # # # # # 
# # # # # # # # # # . . . . . . . . . # # # # # # # # # # # # # # # # # # # # # . # # # # # # # # # 
# # # # # # # # # # . . . . . . . . . # # # # # # # # # # # # # # # # # # # # # . # # # # # # # # # 
# # # # . . . . . . . . . . . . . # # # # # # # # # # # # # # # # . . . . . . . . . . M . . . # # # 
# # # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # . . . . . . . . . . . . . . # # # 
# # # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # . M . . . . . . . . . . . . # # # 
# # # # . # # # # # # # # # # # # # # # # . . . . . . # # # # # # . . . . . . . . . . . . . . # # # 
# # # # . # # # # # # # # # # # # # # # # . M . . . M # # # # # # . . . . . . . . . . . . . . # # # 
# # # # . # # # # # # # # # # # # # # # # . . . . . . # # # # # # . . . . . . . . . . . . . . # # # 
# # # # . # # # # # # # # # # # # # # # # . . . . . . # # # # # # . . . . . . . . . . . . . . # # # 
# # # # . # # # # # # # # # # # # # # . . . . . . . . # # # # # # . . . . . . . . . . . . . . # # # 
# . . . . . . # # # # # # # # # # # # . # . . . . . . # # # # # # . . . . M . . . . . . . . . # # # 
# . . . . . . # # # # # # # # # # # # . # . . . . . . # # # # # # . . . . . . . . . . . . . . # # # 
# . . . . . . # # # # # # # # # # # # . # . . . . . M # # # # # # . . . . . . . . . . . . . . # # # 
# . . . . . . . . . . . . . . . . . . . # . . . . . . # # # # # # # # # # # # # # # # # # # # # # # 
# . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# . . . . . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

잘 된다. 겸사겸사 사인도 M으로 바꿔봤다.

 

아차!

뭔가 복잡해지고 있음을 느낀다.

상점도 만들고 체력도 채우고 싶은데 이번엔 이런 식으로 하다간 방마다 플레이어 스포너가 있는지 검사 -> 아니면 몬스터가 있는지 검사 -> 상점방으로 만들기

뭔가 불편스럽다. 옳지 않음을 느낀다.

그리고 나는 이렇게 완전히 랜덤하게 방을 구성하기 보다는, 몇 개의 "잘 디자인된" 방들을 미리 템플릿으로 만들어 놓고, 거기서 랜덤하게 뽑아와서 서로 연결하는 식으로 맵을 만들고 싶다는 생각을 했다. 왜냐하면 이런 컴퓨터가 만드는 완전 랜덤한 방식보다는 사람인 훌륭한 디자이너(나)가 만든 방이, 사람이 플레이하기에 더 재미있을 확률이 높기 때문이다.

사실 이 내용은 위 참고한 첫번째 포스트의 맨 마지막 부분에 나오는 내용이다. 처음 볼 땐 그냥 그렇구나~ 하고 넘어갔는데 실제로 구현하고 고민해보니 아 실제로 그렇구나 싶다.

피로하다. 다음에 해보자.

반응형